create logical link between users and members closes #164 #172
27 changed files with 2057 additions and 245 deletions
|
|
@ -12,6 +12,12 @@ defmodule Mv.Accounts.User do
|
|||
postgres do
|
||||
table "users"
|
||||
repo Mv.Repo
|
||||
|
||||
references do
|
||||
# When a member is deleted, set the user's member_id to NULL
|
||||
# This allows users to continue existing even if their linked member is removed
|
||||
reference :member, on_delete: :nilify
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
|
@ -60,15 +66,77 @@ defmodule Mv.Accounts.User do
|
|||
end
|
||||
|
||||
actions do
|
||||
defaults [:read, :create, :destroy, :update]
|
||||
# Default actions kept for framework/tooling integration:
|
||||
# - :create -> Used by AshAdmin's generated "Create" UI and by generic
|
||||
# AshPhoenix helpers that assume a default create action.
|
||||
# It does NOT manage the :member relationship. For admin
|
||||
# flows that may link an existing member, use :create_user.
|
||||
# - :read -> Standard read used across the app and by admin tooling.
|
||||
# - :destroy-> Standard delete used by admin tooling and maintenance tasks.
|
||||
defaults [:read, :create, :destroy]
|
||||
|
||||
# Primary generic update action:
|
||||
# - Selected by AshAdmin's generated "Edit" UI and generic AshPhoenix
|
||||
# helpers that assume a default update action.
|
||||
# - Intended for simple attribute updates (e.g., :email) and scenarios
|
||||
# that do NOT need to manage the :member relationship.
|
||||
# - For linking/unlinking a member (and the related validations), prefer
|
||||
# the specialized :update_user action below.
|
||||
update :update do
|
||||
primary? true
|
||||
|
||||
# Required because custom validation functions (email validation, member relationship validation)
|
||||
# cannot be executed atomically. These validations need to query the database and perform
|
||||
# complex checks that are not supported in atomic operations.
|
||||
require_atomic? false
|
||||
end
|
||||
|
||||
create :create_user do
|
||||
description "Creates a new user with optional member relationship. The member relationship is managed through the :member argument."
|
||||
# Only accept email directly - member_id is NOT in accept list
|
||||
# This prevents direct foreign key manipulation, forcing use of manage_relationship
|
||||
accept [:email]
|
||||
# Allow member to be passed as argument for relationship management
|
||||
argument :member, :map, allow_nil?: true
|
||||
upsert? true
|
||||
|
||||
# Manage the member relationship during user creation
|
||||
change manage_relationship(:member, :member,
|
||||
# Look up existing member and relate to it
|
||||
on_lookup: :relate,
|
||||
# Error if member doesn't exist in database
|
||||
on_no_match: :error,
|
||||
# If member already linked to this user, ignore (shouldn't happen in create)
|
||||
on_match: :ignore,
|
||||
# If no member provided, that's fine (optional relationship)
|
||||
on_missing: :ignore
|
||||
)
|
||||
end
|
||||
|
||||
update :update_user do
|
||||
description "Updates a user and manages the optional member relationship. To change an existing member link, first remove it (set member to nil), then add the new one."
|
||||
# Only accept email directly - member_id is NOT in accept list
|
||||
|
moritz marked this conversation as resolved
Outdated
|
||||
# This prevents direct foreign key manipulation, forcing use of manage_relationship
|
||||
accept [:email]
|
||||
# Allow member to be passed as argument for relationship management
|
||||
argument :member, :map, allow_nil?: true
|
||||
|
||||
# Required because custom validation functions (email validation, member relationship validation)
|
||||
# cannot be executed atomically. These validations need to query the database and perform
|
||||
# complex checks that are not supported in atomic operations.
|
||||
require_atomic? false
|
||||
|
||||
# Manage the member relationship during user update
|
||||
change manage_relationship(:member, :member,
|
||||
# Look up existing member and relate to it
|
||||
on_lookup: :relate,
|
||||
# Error if member doesn't exist in database
|
||||
on_no_match: :error,
|
||||
# If same member provided, that's fine (allows updates with same member)
|
||||
on_match: :ignore,
|
||||
# If no member provided, remove existing relationship (allows member removal)
|
||||
on_missing: :unrelate
|
||||
)
|
||||
end
|
||||
|
||||
# Admin action for direct password changes in admin panel
|
||||
|
|
@ -76,6 +144,7 @@ defmodule Mv.Accounts.User do
|
|||
update :admin_set_password do
|
||||
accept [:email]
|
||||
argument :password, :string, allow_nil?: false, sensitive?: true
|
||||
require_atomic? false
|
||||
|
||||
# Set the strategy context that HashPasswordChange expects
|
||||
change set_context(%{strategy_name: :password})
|
||||
|
|
@ -122,8 +191,54 @@ defmodule Mv.Accounts.User do
|
|||
# Global validations - applied to all relevant actions
|
||||
validations do
|
||||
# Password strength policy: minimum 8 characters for all password-related actions
|
||||
validate string_length(:password, min: 8) do
|
||||
where action_is([:register_with_password, :admin_set_password])
|
||||
validate string_length(:password, min: 8),
|
||||
where: [action_is([:register_with_password, :admin_set_password])],
|
||||
message: "must have length of at least 8"
|
||||
|
||||
# Email validation with EctoCommons.EmailValidator (same as Member)
|
||||
# This ensures consistency between User and Member email validation
|
||||
validate fn changeset, _ ->
|
||||
# Get email from attribute (Ash.CiString) and convert to string
|
||||
email = Ash.Changeset.get_attribute(changeset, :email)
|
||||
email_string = if email, do: to_string(email), else: nil
|
||||
|
||||
# Only validate if email is present
|
||||
if email_string do
|
||||
changeset2 =
|
||||
{%{}, %{email: :string}}
|
||||
|> Ecto.Changeset.cast(%{email: email_string}, [:email])
|
||||
|> EctoCommons.EmailValidator.validate_email(:email, checks: [:html_input, :pow])
|
||||
|
||||
if changeset2.valid? do
|
||||
:ok
|
||||
else
|
||||
{:error, field: :email, message: "is not a valid email"}
|
||||
end
|
||||
else
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
# Prevent overwriting existing member relationship
|
||||
# This validation ensures race condition safety by requiring explicit two-step process:
|
||||
# 1. Remove existing member (set member to nil)
|
||||
# 2. Add new member
|
||||
# This prevents accidental overwrites when multiple admins work simultaneously
|
||||
validate fn changeset, _context ->
|
||||
member_arg = Ash.Changeset.get_argument(changeset, :member)
|
||||
current_member_id = changeset.data.member_id
|
||||
|
||||
# Only trigger if:
|
||||
# - member argument is provided AND has an ID
|
||||
# - user currently has a member
|
||||
# - the new member ID is different from current member ID
|
||||
if member_arg && member_arg[:id] && current_member_id &&
|
||||
member_arg[:id] != current_member_id do
|
||||
{:error,
|
||||
field: :member, message: "User already has a member. Remove existing member first."}
|
||||
else
|
||||
:ok
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -140,18 +255,28 @@ defmodule Mv.Accounts.User do
|
|||
attributes do
|
||||
uuid_primary_key :id
|
||||
|
||||
attribute :email, :ci_string, allow_nil?: false, public?: true
|
||||
attribute :email, :ci_string do
|
||||
allow_nil? false
|
||||
public? true
|
||||
# Same constraints as Member email for consistency
|
||||
constraints min_length: 5, max_length: 254
|
||||
end
|
||||
|
||||
attribute :hashed_password, :string, sensitive?: true, allow_nil?: true
|
||||
attribute :oidc_id, :string, allow_nil?: true
|
||||
end
|
||||
|
||||
relationships do
|
||||
# 1:1 relationship - User can optionally belong to one Member
|
||||
# This automatically creates a `member_id` attribute in the User table
|
||||
# The relationship is optional (allow_nil? true by default)
|
||||
belongs_to :member, Mv.Membership.Member
|
||||
end
|
||||
|
||||
identities do
|
||||
identity :unique_email, [:email]
|
||||
identity :unique_oidc_id, [:oidc_id]
|
||||
identity :unique_member, [:member_id]
|
||||
end
|
||||
|
||||
# You can customize this if you wish, but this is a safe default that
|
||||
|
|
|
|||
|
|
@ -13,7 +13,11 @@ defmodule Mv.Membership.Member do
|
|||
|
||||
create :create_member do
|
||||
primary? true
|
||||
# Properties can be created along with member
|
||||
argument :properties, {:array, :map}
|
||||
# Allow user to be passed as argument for relationship management
|
||||
# user_id is NOT in accept list to prevent direct foreign key manipulation
|
||||
argument :user, :map, allow_nil?: true
|
||||
|
||||
accept [
|
||||
:first_name,
|
||||
|
|
@ -32,12 +36,29 @@ defmodule Mv.Membership.Member do
|
|||
]
|
||||
|
||||
change manage_relationship(:properties, type: :create)
|
||||
|
||||
# Manage the user relationship during member creation
|
||||
change manage_relationship(:user, :user,
|
||||
# Look up existing user and relate to it
|
||||
on_lookup: :relate,
|
||||
# Error if user doesn't exist in database
|
||||
on_no_match: :error,
|
||||
# Error if user is already linked to another member (prevents "stealing")
|
||||
on_match: :error,
|
||||
# If no user provided, that's fine (optional relationship)
|
||||
on_missing: :ignore
|
||||
)
|
||||
end
|
||||
|
||||
update :update_member do
|
||||
primary? true
|
||||
# Required because custom validation function cannot be done atomically
|
||||
require_atomic? false
|
||||
# Properties can be updated or created along with member
|
||||
argument :properties, {:array, :map}
|
||||
# Allow user to be passed as argument for relationship management
|
||||
# user_id is NOT in accept list to prevent direct foreign key manipulation
|
||||
argument :user, :map, allow_nil?: true
|
||||
|
||||
accept [
|
||||
:first_name,
|
||||
|
|
@ -56,6 +77,18 @@ defmodule Mv.Membership.Member do
|
|||
]
|
||||
|
||||
change manage_relationship(:properties, on_match: :update, on_no_match: :create)
|
||||
|
||||
# Manage the user relationship during member update
|
||||
change manage_relationship(:user, :user,
|
||||
# Look up existing user and relate to it
|
||||
on_lookup: :relate,
|
||||
# Error if user doesn't exist in database
|
||||
on_no_match: :error,
|
||||
# Error if user is already linked to another member (prevents "stealing")
|
||||
on_match: :error,
|
||||
# If no user provided, remove existing relationship (allows user removal)
|
||||
on_missing: :unrelate
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -67,6 +100,40 @@ defmodule Mv.Membership.Member do
|
|||
validate present(:last_name)
|
||||
validate present(:email)
|
||||
|
||||
# Prevent linking to a user that already has a member
|
||||
# This validation prevents "stealing" users from other members by checking
|
||||
# if the target user is already linked to a different member
|
||||
# This is necessary because manage_relationship's on_match: :error only checks
|
||||
# if the user is already linked to THIS specific member, not ANY member
|
||||
validate fn changeset, _context ->
|
||||
user_arg = Ash.Changeset.get_argument(changeset, :user)
|
||||
|
||||
if user_arg && user_arg[:id] do
|
||||
user_id = user_arg[:id]
|
||||
current_member_id = changeset.data.id
|
||||
|
||||
# Check the current state of the user in the database
|
||||
case Ash.get(Mv.Accounts.User, user_id) do
|
||||
# User is free to be linked
|
||||
{:ok, %{member_id: nil}} ->
|
||||
:ok
|
||||
|
||||
# User already linked to this member (update scenario)
|
||||
{:ok, %{member_id: ^current_member_id}} ->
|
||||
:ok
|
||||
|
||||
{:ok, %{member_id: _other_member_id}} ->
|
||||
# User is linked to a different member - prevent "stealing"
|
||||
{:error, field: :user, message: "User is already linked to another member"}
|
||||
|
||||
{:error, _} ->
|
||||
{:error, field: :user, message: "User not found"}
|
||||
end
|
||||
else
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
# Birth date not in the future
|
||||
validate compare(:birth_date, less_than_or_equal_to: &Date.utc_today/0),
|
||||
where: [present(:birth_date)],
|
||||
|
|
@ -175,5 +242,14 @@ defmodule Mv.Membership.Member do
|
|||
|
||||
relationships do
|
||||
has_many :properties, Mv.Membership.Property
|
||||
# 1:1 relationship - Member can optionally have one User
|
||||
# This references the User's member_id attribute
|
||||
# The relationship is optional (allow_nil? true by default)
|
||||
has_one :user, Mv.Accounts.User
|
||||
end
|
||||
|
||||
# Define identities for upsert operations
|
||||
identities do
|
||||
identity :unique_email, [:email]
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -42,4 +42,10 @@ defmodule Mv.Membership.Property do
|
|||
calculations do
|
||||
calculate :value_to_string, :string, expr(value[:value] <> "")
|
||||
end
|
||||
|
||||
# Ensure a member can only have one property per property type
|
||||
# For example: A member can have only one "email" property, one "phone" property, etc.
|
||||
identities do
|
||||
identity :unique_property_per_member, [:member_id, :property_type_id]
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -16,20 +16,27 @@ defmodule MvWeb.Layouts.Navbar do
|
|||
<div class="flex-1">
|
||||
<a class="btn btn-ghost text-xl">Mitgliederverwaltung</a>
|
||||
<ul class="menu menu-horizontal bg-base-200">
|
||||
<li><a href="/members">{gettext("Members")}</a></li>
|
||||
<li><a>Transaktionen</a></li>
|
||||
<li><.link navigate="/members">{gettext("Members")}</.link></li>
|
||||
<li><.link navigate="/users">{gettext("Users")}</.link></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<form method="post" action="/set_locale" class="mr-4">
|
||||
<input type="hidden" name="_csrf_token" value={Plug.CSRFProtection.get_csrf_token()} />
|
||||
<select name="locale" onchange="this.form.submit()" class="select select-sm">
|
||||
<label class="sr-only" for="locale-select">{gettext("Select language")}</label>
|
||||
<select
|
||||
id="locale-select"
|
||||
name="locale"
|
||||
onchange="this.form.submit()"
|
||||
class="select select-sm"
|
||||
aria-label={gettext("Select language")}
|
||||
>
|
||||
<option value="de" selected={Gettext.get_locale() == "de"}>Deutsch</option>
|
||||
<option value="en" selected={Gettext.get_locale() == "en"}>English</option>
|
||||
</select>
|
||||
</form>
|
||||
<!-- Daisy UI Theme Toggle for dark and light mode-->
|
||||
<label class="flex cursor-pointer gap-2">
|
||||
<label class="flex cursor-pointer gap-2" aria-label={gettext("Toggle dark mode")}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
|
|
@ -40,11 +47,17 @@ defmodule MvWeb.Layouts.Navbar do
|
|||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<circle cx="12" cy="12" r="5" />
|
||||
<path d="M12 1v2M12 21v2M4.2 4.2l1.4 1.4M18.4 18.4l1.4 1.4M1 12h2M21 12h2M4.2 19.8l1.4-1.4M18.4 5.6l1.4-1.4" />
|
||||
</svg>
|
||||
<input type="checkbox" value="dark" class="toggle theme-controller" />
|
||||
<input
|
||||
type="checkbox"
|
||||
value="dark"
|
||||
class="toggle theme-controller"
|
||||
aria-label={gettext("Toggle dark mode")}
|
||||
/>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
|
|
@ -55,6 +68,7 @@ defmodule MvWeb.Layouts.Navbar do
|
|||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
|
||||
</svg>
|
||||
|
|
|
|||
|
|
@ -11,8 +11,9 @@ defmodule MvWeb.MemberLive.Show do
|
|||
<:subtitle>{gettext("This is a member record from your database.")}</:subtitle>
|
||||
|
||||
<:actions>
|
||||
<.button navigate={~p"/members"}>
|
||||
<.button navigate={~p"/members"} aria-label={gettext("Back to members list")}>
|
||||
<.icon name="hero-arrow-left" />
|
||||
<span class="sr-only">{gettext("Back to members list")}</span>
|
||||
</.button>
|
||||
<.button variant="primary" navigate={~p"/members/#{@member}/edit?return_to=show"}>
|
||||
<.icon name="hero-pencil-square" /> {gettext("Edit Member")}
|
||||
|
|
@ -37,6 +38,19 @@ defmodule MvWeb.MemberLive.Show do
|
|||
<:item title={gettext("Street")}>{@member.street}</:item>
|
||||
<:item title={gettext("House Number")}>{@member.house_number}</:item>
|
||||
<:item title={gettext("Postal Code")}>{@member.postal_code}</:item>
|
||||
<:item title={gettext("Linked User")}>
|
||||
<%= if @member.user do %>
|
||||
<.link
|
||||
navigate={~p"/users/#{@member.user}"}
|
||||
class="text-blue-600 hover:text-blue-800 underline"
|
||||
>
|
||||
<.icon name="hero-user" class="h-4 w-4 inline mr-1" />
|
||||
{@member.user.email}
|
||||
</.link>
|
||||
<% else %>
|
||||
<span class="text-gray-500 italic">{gettext("No user linked")}</span>
|
||||
<% end %>
|
||||
</:item>
|
||||
</.list>
|
||||
|
||||
<h3 class="mt-8 mb-2 text-lg font-semibold">{gettext("Custom Properties")}</h3>
|
||||
|
|
@ -67,7 +81,7 @@ defmodule MvWeb.MemberLive.Show do
|
|||
query =
|
||||
Mv.Membership.Member
|
||||
|> filter(id == ^id)
|
||||
|> load(properties: [:property_type])
|
||||
|> load([:user, properties: [:property_type]])
|
||||
|
||||
member = Ash.read_one!(query)
|
||||
|
||||
|
|
|
|||
|
|
@ -10,8 +10,9 @@ defmodule MvWeb.UserLive.Show do
|
|||
<:subtitle>{gettext("This is a user record from your database.")}</:subtitle>
|
||||
|
||||
<:actions>
|
||||
<.button navigate={~p"/users"}>
|
||||
<.button navigate={~p"/users"} aria-label={gettext("Back to users list")}>
|
||||
<.icon name="hero-arrow-left" />
|
||||
<span class="sr-only">{gettext("Back to users list")}</span>
|
||||
</.button>
|
||||
<.button variant="primary" navigate={~p"/users/#{@user}/edit?return_to=show"}>
|
||||
<.icon name="hero-pencil-square" /> {gettext("Edit User")}
|
||||
|
|
@ -26,6 +27,19 @@ defmodule MvWeb.UserLive.Show do
|
|||
<:item title={gettext("Password Authentication")}>
|
||||
{if @user.hashed_password, do: gettext("Enabled"), else: gettext("Not enabled")}
|
||||
</:item>
|
||||
<:item title={gettext("Linked Member")}>
|
||||
<%= if @user.member do %>
|
||||
<.link
|
||||
navigate={~p"/members/#{@user.member}"}
|
||||
class="text-blue-600 hover:text-blue-800 underline"
|
||||
>
|
||||
<.icon name="hero-users" class="h-4 w-4 inline mr-1" />
|
||||
{@user.member.first_name} {@user.member.last_name}
|
||||
</.link>
|
||||
<% else %>
|
||||
<span class="text-gray-500 italic">{gettext("No member linked")}</span>
|
||||
<% end %>
|
||||
</:item>
|
||||
</.list>
|
||||
</Layouts.app>
|
||||
"""
|
||||
|
|
@ -33,9 +47,11 @@ defmodule MvWeb.UserLive.Show do
|
|||
|
||||
@impl true
|
||||
def mount(%{"id" => id}, _session, socket) do
|
||||
user = Ash.get!(Mv.Accounts.User, id, domain: Mv.Accounts, load: [:member])
|
||||
|
||||
{:ok,
|
||||
socket
|
||||
|> assign(:page_title, gettext("Show User"))
|
||||
|> assign(:user, Ash.get!(Mv.Accounts.User, id, domain: Mv.Accounts))}
|
||||
|> assign(:user, user)}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -27,10 +27,10 @@ msgid "Forgot your password?"
|
|||
msgstr "Passwort vergessen?"
|
||||
|
||||
msgid "If this user exists in our database you will contacted with a sign-in link shortly."
|
||||
msgstr "Falls dieser Benutzer bekannt ist, wird jetzt eine Email mit Anmelde-Link versendet."
|
||||
msgstr "Falls diese*r Benutzer*in bekannt ist, wird jetzt eine Email mit Anmelde-Link versendet."
|
||||
|
||||
msgid "If this user exists in our system, you will be contacted with reset instructions shortly."
|
||||
msgstr "Falls dieser Benutzer bekannt ist, wird jetzt eine Email mit einer Anleitung zum Zurücksetzen versendet."
|
||||
msgstr "Falls diese*r Benutzer*in bekannt ist, wird jetzt eine Email mit einer Anleitung zum Zurücksetzen versendet."
|
||||
|
||||
msgid "Need an account?"
|
||||
msgstr "Konto anlegen?"
|
||||
|
|
@ -61,6 +61,3 @@ msgstr "Anmelden..."
|
|||
|
||||
msgid "Your password has successfully been reset"
|
||||
msgstr "Das Passwort wurde erfolgreich zurückgesetzt"
|
||||
|
||||
msgid "Sign in with Rauthy"
|
||||
msgstr "Anmelden mit der Vereinscloud"
|
||||
|
moritz marked this conversation as resolved
Outdated
simon
commented
Is this deletion intentional? Is this deletion intentional?
moritz
commented
yes it was removed by the gettext command, because there is no string to be translated. The login page is another issue anyway. yes it was removed by the gettext command, because there is no string to be translated. The login page is another issue anyway.
|
||||
|
|
|
|||
|
|
@ -15,69 +15,69 @@ msgstr ""
|
|||
msgid "Actions"
|
||||
msgstr "Aktionen"
|
||||
|
||||
#: lib/mv_web/live/member_live/index.html.heex:77
|
||||
#: lib/mv_web/live/member_live/index.html.heex:84
|
||||
#: lib/mv_web/live/user_live/index.html.heex:65
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Are you sure?"
|
||||
msgstr "Bist du sicher?"
|
||||
|
||||
#: lib/mv_web/components/layouts.ex:71
|
||||
#: lib/mv_web/components/layouts.ex:83
|
||||
#: lib/mv_web/components/layouts.ex:80
|
||||
#: lib/mv_web/components/layouts.ex:92
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Attempting to reconnect"
|
||||
msgstr "Verbindung wird wiederhergestellt"
|
||||
|
||||
#: lib/mv_web/live/member_live/form.ex:25
|
||||
#: lib/mv_web/live/member_live/index.html.heex:62
|
||||
#: lib/mv_web/live/member_live/show.ex:36
|
||||
#: lib/mv_web/live/member_live/index.html.heex:69
|
||||
#: lib/mv_web/live/member_live/show.ex:37
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "City"
|
||||
msgstr "Stadt"
|
||||
|
||||
#: lib/mv_web/live/member_live/index.html.heex:79
|
||||
#: lib/mv_web/live/member_live/index.html.heex:86
|
||||
#: lib/mv_web/live/user_live/index.html.heex:67
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Delete"
|
||||
msgstr "Löschen"
|
||||
|
||||
#: lib/mv_web/live/member_live/index.html.heex:71
|
||||
#: lib/mv_web/live/member_live/index.html.heex:78
|
||||
#: lib/mv_web/live/user_live/form.ex:109
|
||||
#: lib/mv_web/live/user_live/index.html.heex:59
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Edit"
|
||||
msgstr "Bearbeite"
|
||||
|
||||
#: lib/mv_web/live/member_live/show.ex:18
|
||||
#: lib/mv_web/live/member_live/show.ex:81
|
||||
#: lib/mv_web/live/member_live/show.ex:19
|
||||
#: lib/mv_web/live/member_live/show.ex:95
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Edit Member"
|
||||
msgstr "Mitglied bearbeiten"
|
||||
|
||||
#: lib/mv_web/live/member_live/form.ex:18
|
||||
#: lib/mv_web/live/member_live/index.html.heex:58
|
||||
#: lib/mv_web/live/member_live/show.ex:27
|
||||
#: lib/mv_web/live/member_live/index.html.heex:65
|
||||
#: lib/mv_web/live/member_live/show.ex:28
|
||||
#: lib/mv_web/live/user_live/form.ex:14
|
||||
#: lib/mv_web/live/user_live/index.html.heex:44
|
||||
#: lib/mv_web/live/user_live/show.ex:24
|
||||
#: lib/mv_web/live/user_live/show.ex:25
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Email"
|
||||
msgstr "E-Mail"
|
||||
|
||||
#: lib/mv_web/live/member_live/form.ex:16
|
||||
#: lib/mv_web/live/member_live/show.ex:25
|
||||
#: lib/mv_web/live/member_live/show.ex:26
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "First Name"
|
||||
msgstr "Vorname"
|
||||
|
||||
#: lib/mv_web/live/member_live/form.ex:22
|
||||
#: lib/mv_web/live/member_live/index.html.heex:64
|
||||
#: lib/mv_web/live/member_live/show.ex:33
|
||||
#: lib/mv_web/live/member_live/index.html.heex:71
|
||||
#: lib/mv_web/live/member_live/show.ex:34
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Join Date"
|
||||
msgstr "Beitrittsdatum"
|
||||
|
||||
#: lib/mv_web/live/member_live/form.ex:17
|
||||
#: lib/mv_web/live/member_live/show.ex:26
|
||||
#: lib/mv_web/live/member_live/show.ex:27
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Last Name"
|
||||
msgstr "Nachname"
|
||||
|
|
@ -87,18 +87,18 @@ msgstr "Nachname"
|
|||
msgid "New Member"
|
||||
msgstr "Neues Mitglied"
|
||||
|
||||
#: lib/mv_web/live/member_live/index.html.heex:68
|
||||
#: lib/mv_web/live/member_live/index.html.heex:75
|
||||
#: lib/mv_web/live/user_live/index.html.heex:56
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Show"
|
||||
msgstr "Anzeigen"
|
||||
|
||||
#: lib/mv_web/components/layouts.ex:78
|
||||
#: lib/mv_web/components/layouts.ex:87
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Something went wrong!"
|
||||
msgstr "Etwas ist schiefgelaufen!"
|
||||
|
||||
#: lib/mv_web/components/layouts.ex:66
|
||||
#: lib/mv_web/components/layouts.ex:75
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "We can't find the internet"
|
||||
msgstr "Keine Internetverbindung gefunden"
|
||||
|
|
@ -109,52 +109,52 @@ msgid "close"
|
|||
msgstr "schließen"
|
||||
|
||||
#: lib/mv_web/live/member_live/form.ex:19
|
||||
#: lib/mv_web/live/member_live/show.ex:28
|
||||
#: lib/mv_web/live/member_live/show.ex:29
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Birth Date"
|
||||
msgstr "Geburtsdatum"
|
||||
|
||||
#: lib/mv_web/live/member_live/form.ex:30
|
||||
#: lib/mv_web/live/member_live/show.ex:42
|
||||
#: lib/mv_web/live/member_live/show.ex:56
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Custom Properties"
|
||||
msgstr "Eigene Eigenschaften"
|
||||
|
||||
#: lib/mv_web/live/member_live/form.ex:23
|
||||
#: lib/mv_web/live/member_live/show.ex:34
|
||||
#: lib/mv_web/live/member_live/show.ex:35
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Exit Date"
|
||||
msgstr "Austrittsdatum"
|
||||
|
||||
#: lib/mv_web/live/member_live/form.ex:27
|
||||
#: lib/mv_web/live/member_live/index.html.heex:60
|
||||
#: lib/mv_web/live/member_live/show.ex:38
|
||||
#: lib/mv_web/live/member_live/index.html.heex:67
|
||||
#: lib/mv_web/live/member_live/show.ex:39
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "House Number"
|
||||
msgstr "Hausnummer"
|
||||
|
||||
#: lib/mv_web/live/member_live/form.ex:24
|
||||
#: lib/mv_web/live/member_live/show.ex:35
|
||||
#: lib/mv_web/live/member_live/show.ex:36
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Notes"
|
||||
msgstr "Notizen"
|
||||
|
||||
#: lib/mv_web/live/member_live/form.ex:20
|
||||
#: lib/mv_web/live/member_live/show.ex:29
|
||||
#: lib/mv_web/live/member_live/show.ex:30
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Paid"
|
||||
msgstr "Bezahlt"
|
||||
|
||||
#: lib/mv_web/live/member_live/form.ex:21
|
||||
#: lib/mv_web/live/member_live/index.html.heex:63
|
||||
#: lib/mv_web/live/member_live/show.ex:32
|
||||
#: lib/mv_web/live/member_live/index.html.heex:70
|
||||
#: lib/mv_web/live/member_live/show.ex:33
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Phone Number"
|
||||
msgstr "Telefonnummer"
|
||||
|
||||
#: lib/mv_web/live/member_live/form.ex:28
|
||||
#: lib/mv_web/live/member_live/index.html.heex:61
|
||||
#: lib/mv_web/live/member_live/show.ex:39
|
||||
#: lib/mv_web/live/member_live/index.html.heex:68
|
||||
#: lib/mv_web/live/member_live/show.ex:40
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Postal Code"
|
||||
msgstr "Postleitzahl"
|
||||
|
|
@ -173,8 +173,8 @@ msgid "Saving..."
|
|||
msgstr "Speichern..."
|
||||
|
||||
#: lib/mv_web/live/member_live/form.ex:26
|
||||
#: lib/mv_web/live/member_live/index.html.heex:59
|
||||
#: lib/mv_web/live/member_live/show.ex:37
|
||||
#: lib/mv_web/live/member_live/index.html.heex:66
|
||||
#: lib/mv_web/live/member_live/show.ex:38
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Street"
|
||||
msgstr "Straße"
|
||||
|
|
@ -184,17 +184,17 @@ msgstr "Straße"
|
|||
msgid "Use this form to manage member records and their properties."
|
||||
msgstr "Dieses Formular dient zur Verwaltung von Mitgliedern und deren Eigenschaften."
|
||||
|
||||
#: lib/mv_web/live/member_live/show.ex:24
|
||||
#: lib/mv_web/live/member_live/show.ex:25
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Id"
|
||||
msgstr "ID"
|
||||
|
||||
#: lib/mv_web/live/member_live/show.ex:30
|
||||
#: lib/mv_web/live/member_live/show.ex:31
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "No"
|
||||
msgstr "Nein"
|
||||
|
||||
#: lib/mv_web/live/member_live/show.ex:80
|
||||
#: lib/mv_web/live/member_live/show.ex:94
|
||||
#, elixir-autogen, elixir-format, fuzzy
|
||||
msgid "Show Member"
|
||||
msgstr "Mitglied anzeigen"
|
||||
|
|
@ -204,7 +204,7 @@ msgstr "Mitglied anzeigen"
|
|||
msgid "This is a member record from your database."
|
||||
msgstr "Dies ist ein Mitglied aus deiner Datenbank."
|
||||
|
||||
#: lib/mv_web/live/member_live/show.ex:30
|
||||
#: lib/mv_web/live/member_live/show.ex:31
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Yes"
|
||||
msgstr "Ja"
|
||||
|
|
@ -281,17 +281,17 @@ msgstr "Eigenschaftstyp auswählen"
|
|||
msgid "Description"
|
||||
msgstr "Beschreibung"
|
||||
|
||||
#: lib/mv_web/live/user_live/show.ex:17
|
||||
#: lib/mv_web/live/user_live/show.ex:18
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Edit User"
|
||||
msgstr "Benutzer bearbeiten"
|
||||
msgstr "Benutzer*in bearbeiten"
|
||||
|
||||
#: lib/mv_web/live/user_live/show.ex:27
|
||||
#: lib/mv_web/live/user_live/show.ex:28
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Enabled"
|
||||
msgstr "Aktiviert"
|
||||
|
||||
#: lib/mv_web/live/user_live/show.ex:23
|
||||
#: lib/mv_web/live/user_live/show.ex:24
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "ID"
|
||||
msgstr "ID"
|
||||
|
|
@ -301,7 +301,7 @@ msgstr "ID"
|
|||
msgid "Immutable"
|
||||
msgstr "Unveränderlich"
|
||||
|
||||
#: lib/mv_web/components/layouts/navbar.ex:73
|
||||
#: lib/mv_web/components/layouts/navbar.ex:94
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Logout"
|
||||
msgstr "Abmelden"
|
||||
|
|
@ -310,21 +310,21 @@ msgstr "Abmelden"
|
|||
#: lib/mv_web/live/user_live/index.html.heex:3
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Listing Users"
|
||||
msgstr "Benutzer auflisten"
|
||||
msgstr "Benutzer*innen auflisten"
|
||||
|
||||
#: lib/mv_web/live/property_live/form.ex:27
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Member"
|
||||
msgstr "Mitglied"
|
||||
|
||||
#: lib/mv_web/components/layouts/navbar.ex:14
|
||||
#: lib/mv_web/live/member_live/index.ex:12
|
||||
#: lib/mv_web/components/layouts/navbar.ex:19
|
||||
#: lib/mv_web/live/member_live/index.ex:14
|
||||
#: lib/mv_web/live/member_live/index.html.heex:3
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Members"
|
||||
msgstr "Mitglieder"
|
||||
|
||||
#: lib/mv_web/live/member_live/index.html.heex:50
|
||||
#: lib/mv_web/live/member_live/index.html.heex:57
|
||||
#: lib/mv_web/live/property_type_live/form.ex:16
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Name"
|
||||
|
|
@ -333,14 +333,14 @@ msgstr "Name"
|
|||
#: lib/mv_web/live/user_live/index.html.heex:6
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "New User"
|
||||
msgstr "Neuer Benutzer"
|
||||
msgstr "Neue*r Benutzer*in"
|
||||
|
||||
#: lib/mv_web/live/user_live/show.ex:27
|
||||
#: lib/mv_web/live/user_live/show.ex:28
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Not enabled"
|
||||
msgstr "Nicht aktiviert"
|
||||
|
||||
#: lib/mv_web/live/user_live/show.ex:25
|
||||
#: lib/mv_web/live/user_live/show.ex:26
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Not set"
|
||||
msgstr "Nicht gesetzt"
|
||||
|
|
@ -352,12 +352,12 @@ msgid "Note"
|
|||
msgstr "Hinweis"
|
||||
|
||||
#: lib/mv_web/live/user_live/index.html.heex:52
|
||||
#: lib/mv_web/live/user_live/show.ex:25
|
||||
#: lib/mv_web/live/user_live/show.ex:26
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "OIDC ID"
|
||||
msgstr "OIDC ID"
|
||||
|
||||
#: lib/mv_web/live/user_live/show.ex:26
|
||||
#: lib/mv_web/live/user_live/show.ex:27
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Password Authentication"
|
||||
msgstr "Passwort-Authentifizierung"
|
||||
|
|
@ -367,15 +367,15 @@ msgstr "Passwort-Authentifizierung"
|
|||
msgid "Please select a property type first"
|
||||
msgstr "Bitte wählen Sie zuerst einen Eigenschaftstyp"
|
||||
|
||||
#: lib/mv_web/components/layouts/navbar.ex:69
|
||||
#: lib/mv_web/components/layouts/navbar.ex:89
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Profil"
|
||||
msgstr "Profil"
|
||||
|
||||
#: lib/mv_web/live/property_live/form.ex:207
|
||||
#, elixir-autogen, elixir-format, fuzzy
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Property %{action} successfully"
|
||||
msgstr "Mitglied %{action} erfolgreich"
|
||||
msgstr "Eigenschaft %{action} erfolgreich"
|
||||
|
||||
#: lib/mv_web/live/property_live/form.ex:18
|
||||
#, elixir-autogen, elixir-format
|
||||
|
|
@ -402,17 +402,17 @@ msgstr "Eigenschaft speichern"
|
|||
msgid "Save Property type"
|
||||
msgstr "Eigenschaftstyp speichern"
|
||||
|
||||
#: lib/mv_web/live/member_live/index.html.heex:27
|
||||
#: lib/mv_web/live/member_live/index.html.heex:34
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Select all members"
|
||||
msgstr "Alle Mitglieder auswählen"
|
||||
|
||||
#: lib/mv_web/live/member_live/index.html.heex:41
|
||||
#: lib/mv_web/live/member_live/index.html.heex:48
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Select member"
|
||||
msgstr "Mitglied auswählen"
|
||||
|
||||
#: lib/mv_web/components/layouts/navbar.ex:72
|
||||
#: lib/mv_web/components/layouts/navbar.ex:92
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Settings"
|
||||
msgstr "Einstellungen"
|
||||
|
|
@ -420,17 +420,17 @@ msgstr "Einstellungen"
|
|||
#: lib/mv_web/live/user_live/form.ex:93
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Save User"
|
||||
msgstr "Benutzer speichern"
|
||||
msgstr "Benutzer*in speichern"
|
||||
|
||||
#: lib/mv_web/live/user_live/show.ex:38
|
||||
#: lib/mv_web/live/user_live/show.ex:54
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Show User"
|
||||
msgstr "Benutzer anzeigen"
|
||||
msgstr "Benutzer*in anzeigen"
|
||||
|
||||
#: lib/mv_web/live/user_live/show.ex:10
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "This is a user record from your database."
|
||||
msgstr "Dies ist ein Benutzer-Datensatz aus Ihrer Datenbank."
|
||||
msgstr "Dies ist ein Benutzer*innen-Datensatz aus Ihrer Datenbank."
|
||||
|
||||
#: lib/mv_web/live/property_live/form.ex:95
|
||||
#, elixir-autogen, elixir-format
|
||||
|
|
@ -438,25 +438,25 @@ msgid "Unsupported value type: %{type}"
|
|||
msgstr "Nicht unterstützter Wertetyp: %{type}"
|
||||
|
||||
#: lib/mv_web/live/property_live/form.ex:10
|
||||
#, elixir-autogen, elixir-format, fuzzy
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Use this form to manage property records in your database."
|
||||
msgstr "Dieses Formular dient zur Verwaltung von Mitgliedern und deren Eigenschaften."
|
||||
msgstr "Dieses Formular dient zur Verwaltung von Eigenschaften in der Datenbank."
|
||||
|
||||
#: lib/mv_web/live/property_type_live/form.ex:11
|
||||
#, elixir-autogen, elixir-format, fuzzy
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Use this form to manage property_type records in your database."
|
||||
msgstr "Dieses Formular dient zur Verwaltung von Mitgliedern und deren Eigenschaften."
|
||||
msgstr "Dieses Formular dient zur Verwaltung von Eigenschaftstypen in der Datenbank."
|
||||
|
||||
#: lib/mv_web/live/user_live/form.ex:10
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Use this form to manage user records in your database."
|
||||
msgstr "Verwenden Sie dieses Formular, um Benutzer-Datensätze zu verwalten."
|
||||
msgstr "Verwenden Sie dieses Formular, um Benutzer*innen-Datensätze zu verwalten."
|
||||
|
||||
#: lib/mv_web/live/user_live/form.ex:110
|
||||
#: lib/mv_web/live/user_live/show.ex:9
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "User"
|
||||
msgstr "Benutzer"
|
||||
msgstr "Benutzer*in"
|
||||
|
||||
#: lib/mv_web/live/property_live/form.ex:59
|
||||
#, elixir-autogen, elixir-format
|
||||
|
|
@ -481,17 +481,17 @@ msgstr "absteigend"
|
|||
#: lib/mv_web/live/user_live/form.ex:109
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "New"
|
||||
msgstr "Neuer"
|
||||
msgstr "Neue*r"
|
||||
|
||||
#: lib/mv_web/live/user_live/form.ex:64
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Admin Note"
|
||||
msgstr "Administrator-Hinweis"
|
||||
msgstr "Administrator*innen-Hinweis"
|
||||
|
||||
#: lib/mv_web/live/user_live/form.ex:64
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "As an administrator, you can directly set a new password for this user using the same secure Ash Authentication system."
|
||||
msgstr "Als Administrator können Sie direkt ein neues Passwort für diesen Benutzer setzen, wobei das gleiche sichere Ash Authentication System verwendet wird."
|
||||
msgstr "Als Administrator*in können Sie direkt ein neues Passwort für diese*n Benutzer*in setzen, wobei das gleiche sichere Ash Authentication System verwendet wird."
|
||||
|
||||
#: lib/mv_web/live/user_live/form.ex:55
|
||||
#, elixir-autogen, elixir-format
|
||||
|
|
@ -506,7 +506,7 @@ msgstr "Passwort ändern"
|
|||
#: lib/mv_web/live/user_live/form.ex:75
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Check 'Change Password' above to set a new password for this user."
|
||||
msgstr "Aktivieren Sie 'Passwort ändern' oben, um ein neues Passwort für diesen Benutzer zu setzen."
|
||||
msgstr "Aktivieren Sie 'Passwort ändern' oben, um ein neues Passwort für diese*n Benutzer*in zu setzen."
|
||||
|
||||
#: lib/mv_web/live/user_live/form.ex:45
|
||||
#, elixir-autogen, elixir-format
|
||||
|
|
@ -536,12 +536,12 @@ msgstr "Passwort-Anforderungen"
|
|||
#: lib/mv_web/live/user_live/index.html.heex:21
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Select all users"
|
||||
msgstr "Alle Benutzer auswählen"
|
||||
msgstr "Alle Benutzer*innen auswählen"
|
||||
|
||||
#: lib/mv_web/live/user_live/index.html.heex:35
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Select user"
|
||||
msgstr "Benutzer auswählen"
|
||||
msgstr "Benutzer*in auswählen"
|
||||
|
||||
#: lib/mv_web/live/user_live/form.ex:27
|
||||
#, elixir-autogen, elixir-format
|
||||
|
|
@ -551,9 +551,59 @@ msgstr "Passwort setzen"
|
|||
#: lib/mv_web/live/user_live/form.ex:83
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "User will be created without a password. Check 'Set Password' to add one."
|
||||
msgstr "Benutzer wird ohne Passwort erstellt. Aktivieren Sie 'Passwort setzen', um eines hinzuzufügen."
|
||||
msgstr "Benutzer*in wird ohne Passwort erstellt. Aktivieren Sie 'Passwort setzen', um eines hinzuzufügen."
|
||||
|
||||
#: lib/mv_web/auth_overrides.ex:30
|
||||
#: lib/mv_web/live/user_live/show.ex:30
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "or"
|
||||
msgstr "oder"
|
||||
msgid "Linked Member"
|
||||
msgstr "Verknüpftes Mitglied"
|
||||
|
||||
#: lib/mv_web/live/member_live/show.ex:41
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Linked User"
|
||||
msgstr "Verknüpfte*r Benutzer*in"
|
||||
|
moritz marked this conversation as resolved
Outdated
simon
commented
See our agreement on using gender-sensitive language. See our agreement on using gender-sensitive language.
But is still unclear whether e.g. Benutzer*in or Benutzer:in - see issue on our agenda for thursday
|
||||
|
||||
#: lib/mv_web/live/user_live/show.ex:40
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "No member linked"
|
||||
msgstr "Kein Mitglied verknüpft"
|
||||
|
||||
#: lib/mv_web/live/member_live/show.ex:51
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "No user linked"
|
||||
msgstr "Keine*r Benutzer*in verknüpft"
|
||||
|
||||
#: lib/mv_web/live/member_live/show.ex:14
|
||||
#: lib/mv_web/live/member_live/show.ex:16
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Back to members list"
|
||||
msgstr "Zurück zur Mitgliederliste"
|
||||
|
||||
#: lib/mv_web/live/user_live/show.ex:13
|
||||
#: lib/mv_web/live/user_live/show.ex:15
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Back to users list"
|
||||
msgstr "Zurück zur Benutzer*innen-Liste"
|
||||
|
||||
#: lib/mv_web/components/layouts/navbar.ex:27
|
||||
#: lib/mv_web/components/layouts/navbar.ex:33
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Select language"
|
||||
msgstr "Sprache auswählen"
|
||||
|
||||
#: lib/mv_web/components/layouts/navbar.ex:40
|
||||
#: lib/mv_web/components/layouts/navbar.ex:60
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Toggle dark mode"
|
||||
msgstr "Dunklen Modus umschalten"
|
||||
|
||||
#: lib/mv_web/live/components/search_bar_component.ex:15
|
||||
#: lib/mv_web/live/member_live/index.html.heex:15
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Search..."
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/components/layouts/navbar.ex:20
|
||||
#, elixir-autogen, elixir-format, fuzzy
|
||||
msgid "Users"
|
||||
msgstr "Benutzer*innen"
|
||||
|
|
|
|||
|
|
@ -12,101 +12,146 @@ msgstr ""
|
|||
|
||||
## From Ecto.Changeset.cast/4
|
||||
msgid "can't be blank"
|
||||
msgstr ""
|
||||
msgstr "darf nicht leer sein"
|
||||
|
||||
## From Ecto.Changeset.unique_constraint/3
|
||||
msgid "has already been taken"
|
||||
msgstr ""
|
||||
msgstr "ist bereits vergeben"
|
||||
|
||||
## From Ecto.Changeset.put_change/3
|
||||
msgid "is invalid"
|
||||
msgstr ""
|
||||
msgstr "ist ungültig"
|
||||
|
||||
## From Ecto.Changeset.validate_acceptance/3
|
||||
msgid "must be accepted"
|
||||
msgstr ""
|
||||
msgstr "muss akzeptiert werden"
|
||||
|
||||
## From Ecto.Changeset.validate_format/3
|
||||
msgid "has invalid format"
|
||||
msgstr ""
|
||||
msgstr "hat ein ungültiges Format"
|
||||
|
||||
## From Ecto.Changeset.validate_subset/3
|
||||
msgid "has an invalid entry"
|
||||
msgstr ""
|
||||
msgstr "hat einen ungültigen Eintrag"
|
||||
|
||||
## From Ecto.Changeset.validate_exclusion/3
|
||||
msgid "is reserved"
|
||||
msgstr ""
|
||||
msgstr "ist reserviert"
|
||||
|
||||
## From Ecto.Changeset.validate_confirmation/3
|
||||
msgid "does not match confirmation"
|
||||
msgstr ""
|
||||
msgstr "stimmt nicht mit der Bestätigung überein"
|
||||
|
||||
## From Ecto.Changeset.no_assoc_constraint/3
|
||||
msgid "is still associated with this entry"
|
||||
msgstr ""
|
||||
msgstr "ist noch mit diesem Eintrag verknüpft"
|
||||
|
||||
msgid "are still associated with this entry"
|
||||
msgstr ""
|
||||
msgstr "sind noch mit diesem Eintrag verknüpft"
|
||||
|
||||
## From Ecto.Changeset.validate_length/3
|
||||
msgid "should have %{count} item(s)"
|
||||
msgid_plural "should have %{count} item(s)"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
msgstr[0] "sollte %{count} Element haben"
|
||||
msgstr[1] "sollte %{count} Elemente haben"
|
||||
|
||||
msgid "should be %{count} character(s)"
|
||||
msgid_plural "should be %{count} character(s)"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
msgstr[0] "sollte %{count} Zeichen haben"
|
||||
msgstr[1] "sollte %{count} Zeichen haben"
|
||||
|
||||
msgid "should be %{count} byte(s)"
|
||||
msgid_plural "should be %{count} byte(s)"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
msgstr[0] "sollte %{count} Byte haben"
|
||||
msgstr[1] "sollte %{count} Bytes haben"
|
||||
|
||||
msgid "should have at least %{count} item(s)"
|
||||
msgid_plural "should have at least %{count} item(s)"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
msgstr[0] "sollte mindestens %{count} Element haben"
|
||||
msgstr[1] "sollte mindestens %{count} Elemente haben"
|
||||
|
||||
msgid "should be at least %{count} character(s)"
|
||||
msgid_plural "should be at least %{count} character(s)"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
msgstr[0] "sollte mindestens %{count} Zeichen haben"
|
||||
msgstr[1] "sollte mindestens %{count} Zeichen haben"
|
||||
|
||||
msgid "should be at least %{count} byte(s)"
|
||||
msgid_plural "should be at least %{count} byte(s)"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
msgstr[0] "sollte mindestens %{count} Byte haben"
|
||||
msgstr[1] "sollte mindestens %{count} Bytes haben"
|
||||
|
||||
msgid "should have at most %{count} item(s)"
|
||||
msgid_plural "should have at most %{count} item(s)"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
msgstr[0] "sollte höchstens %{count} Element haben"
|
||||
msgstr[1] "sollte höchstens %{count} Elemente haben"
|
||||
|
||||
msgid "should be at most %{count} character(s)"
|
||||
msgid_plural "should be at most %{count} character(s)"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
msgstr[0] "sollte höchstens %{count} Zeichen haben"
|
||||
msgstr[1] "sollte höchstens %{count} Zeichen haben"
|
||||
|
||||
msgid "should be at most %{count} byte(s)"
|
||||
msgid_plural "should be at most %{count} byte(s)"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
msgstr[0] "sollte höchstens %{count} Byte haben"
|
||||
msgstr[1] "sollte höchstens %{count} Bytes haben"
|
||||
|
||||
## From Ecto.Changeset.validate_number/3
|
||||
msgid "must be less than %{number}"
|
||||
msgstr ""
|
||||
msgstr "muss kleiner als %{number} sein"
|
||||
|
||||
msgid "must be greater than %{number}"
|
||||
msgstr ""
|
||||
msgstr "muss größer als %{number} sein"
|
||||
|
||||
msgid "must be less than or equal to %{number}"
|
||||
msgstr ""
|
||||
msgstr "muss kleiner oder gleich %{number} sein"
|
||||
|
||||
msgid "must be greater than or equal to %{number}"
|
||||
msgstr ""
|
||||
msgstr "muss größer oder gleich %{number} sein"
|
||||
|
||||
msgid "must be equal to %{number}"
|
||||
msgstr ""
|
||||
msgstr "muss gleich %{number} sein"
|
||||
|
||||
## Ash Framework - Standard constraint messages
|
||||
msgid "length must be greater than or equal to %{min}"
|
||||
msgstr "muss mindestens %{min} Zeichen lang sein"
|
||||
|
||||
msgid "length must be less than or equal to %{max}"
|
||||
msgstr "darf höchstens %{max} Zeichen lang sein"
|
||||
|
||||
msgid "must be present"
|
||||
msgstr "muss vorhanden sein"
|
||||
|
||||
## Custom validation messages from Mv.Accounts.User
|
||||
msgid "User already has a member. Remove existing member first."
|
||||
msgstr "Benutzer*in hat bereits ein Mitglied. Entfernen Sie zuerst das vorhandene Mitglied."
|
||||
|
||||
msgid "OIDC user_info must contain a non-empty 'sub' or 'id' field"
|
||||
msgstr "OIDC user_info darf kein leeres 'sub' oder 'id' Feld enthalten"
|
||||
|
||||
## Custom validation messages from Mv.Membership.Member
|
||||
msgid "User is already linked to another member"
|
||||
msgstr "Benutzer*in ist bereits mit einem anderen Mitglied verknüpft"
|
||||
|
||||
msgid "User not found"
|
||||
msgstr "Benutzer*in nicht gefunden"
|
||||
|
||||
msgid "cannot be in the future"
|
||||
msgstr "darf nicht in der Zukunft liegen"
|
||||
|
||||
msgid "cannot be before join date"
|
||||
msgstr "darf nicht vor dem Beitrittsdatum liegen"
|
||||
|
||||
msgid "is not a valid phone number"
|
||||
msgstr "ist keine gültige Telefonnummer"
|
||||
|
||||
msgid "must consist of 5 digits"
|
||||
msgstr "muss aus 5 Ziffern bestehen"
|
||||
|
||||
msgid "is not a valid email"
|
||||
msgstr "ist keine gültige E-Mail-Adresse"
|
||||
|
||||
msgid "must have length of at least 8"
|
||||
msgstr "muss mindestens 8 Zeichen lang sein"
|
||||
|
||||
msgid "is required"
|
||||
msgstr "ist erforderlich"
|
||||
|
|
|
|||
|
|
@ -16,69 +16,69 @@ msgstr ""
|
|||
msgid "Actions"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/index.html.heex:77
|
||||
#: lib/mv_web/live/member_live/index.html.heex:84
|
||||
#: lib/mv_web/live/user_live/index.html.heex:65
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Are you sure?"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/components/layouts.ex:71
|
||||
#: lib/mv_web/components/layouts.ex:83
|
||||
#: lib/mv_web/components/layouts.ex:80
|
||||
#: lib/mv_web/components/layouts.ex:92
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Attempting to reconnect"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/form.ex:25
|
||||
#: lib/mv_web/live/member_live/index.html.heex:62
|
||||
#: lib/mv_web/live/member_live/show.ex:36
|
||||
#: lib/mv_web/live/member_live/index.html.heex:69
|
||||
#: lib/mv_web/live/member_live/show.ex:37
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "City"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/index.html.heex:79
|
||||
#: lib/mv_web/live/member_live/index.html.heex:86
|
||||
#: lib/mv_web/live/user_live/index.html.heex:67
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Delete"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/index.html.heex:71
|
||||
#: lib/mv_web/live/member_live/index.html.heex:78
|
||||
#: lib/mv_web/live/user_live/form.ex:109
|
||||
#: lib/mv_web/live/user_live/index.html.heex:59
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Edit"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/show.ex:18
|
||||
#: lib/mv_web/live/member_live/show.ex:81
|
||||
#: lib/mv_web/live/member_live/show.ex:19
|
||||
#: lib/mv_web/live/member_live/show.ex:95
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Edit Member"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/form.ex:18
|
||||
#: lib/mv_web/live/member_live/index.html.heex:58
|
||||
#: lib/mv_web/live/member_live/show.ex:27
|
||||
#: lib/mv_web/live/member_live/index.html.heex:65
|
||||
#: lib/mv_web/live/member_live/show.ex:28
|
||||
#: lib/mv_web/live/user_live/form.ex:14
|
||||
#: lib/mv_web/live/user_live/index.html.heex:44
|
||||
#: lib/mv_web/live/user_live/show.ex:24
|
||||
#: lib/mv_web/live/user_live/show.ex:25
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Email"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/form.ex:16
|
||||
#: lib/mv_web/live/member_live/show.ex:25
|
||||
#: lib/mv_web/live/member_live/show.ex:26
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "First Name"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/form.ex:22
|
||||
#: lib/mv_web/live/member_live/index.html.heex:64
|
||||
#: lib/mv_web/live/member_live/show.ex:33
|
||||
#: lib/mv_web/live/member_live/index.html.heex:71
|
||||
#: lib/mv_web/live/member_live/show.ex:34
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Join Date"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/form.ex:17
|
||||
#: lib/mv_web/live/member_live/show.ex:26
|
||||
#: lib/mv_web/live/member_live/show.ex:27
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Last Name"
|
||||
msgstr ""
|
||||
|
|
@ -88,18 +88,18 @@ msgstr ""
|
|||
msgid "New Member"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/index.html.heex:68
|
||||
#: lib/mv_web/live/member_live/index.html.heex:75
|
||||
#: lib/mv_web/live/user_live/index.html.heex:56
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Show"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/components/layouts.ex:78
|
||||
#: lib/mv_web/components/layouts.ex:87
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Something went wrong!"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/components/layouts.ex:66
|
||||
#: lib/mv_web/components/layouts.ex:75
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "We can't find the internet"
|
||||
msgstr ""
|
||||
|
|
@ -110,52 +110,52 @@ msgid "close"
|
|||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/form.ex:19
|
||||
#: lib/mv_web/live/member_live/show.ex:28
|
||||
#: lib/mv_web/live/member_live/show.ex:29
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Birth Date"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/form.ex:30
|
||||
#: lib/mv_web/live/member_live/show.ex:42
|
||||
#: lib/mv_web/live/member_live/show.ex:56
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Custom Properties"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/form.ex:23
|
||||
#: lib/mv_web/live/member_live/show.ex:34
|
||||
#: lib/mv_web/live/member_live/show.ex:35
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Exit Date"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/form.ex:27
|
||||
#: lib/mv_web/live/member_live/index.html.heex:60
|
||||
#: lib/mv_web/live/member_live/show.ex:38
|
||||
#: lib/mv_web/live/member_live/index.html.heex:67
|
||||
#: lib/mv_web/live/member_live/show.ex:39
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "House Number"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/form.ex:24
|
||||
#: lib/mv_web/live/member_live/show.ex:35
|
||||
#: lib/mv_web/live/member_live/show.ex:36
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Notes"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/form.ex:20
|
||||
#: lib/mv_web/live/member_live/show.ex:29
|
||||
#: lib/mv_web/live/member_live/show.ex:30
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Paid"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/form.ex:21
|
||||
#: lib/mv_web/live/member_live/index.html.heex:63
|
||||
#: lib/mv_web/live/member_live/show.ex:32
|
||||
#: lib/mv_web/live/member_live/index.html.heex:70
|
||||
#: lib/mv_web/live/member_live/show.ex:33
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Phone Number"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/form.ex:28
|
||||
#: lib/mv_web/live/member_live/index.html.heex:61
|
||||
#: lib/mv_web/live/member_live/show.ex:39
|
||||
#: lib/mv_web/live/member_live/index.html.heex:68
|
||||
#: lib/mv_web/live/member_live/show.ex:40
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Postal Code"
|
||||
msgstr ""
|
||||
|
|
@ -174,8 +174,8 @@ msgid "Saving..."
|
|||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/form.ex:26
|
||||
#: lib/mv_web/live/member_live/index.html.heex:59
|
||||
#: lib/mv_web/live/member_live/show.ex:37
|
||||
#: lib/mv_web/live/member_live/index.html.heex:66
|
||||
#: lib/mv_web/live/member_live/show.ex:38
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Street"
|
||||
msgstr ""
|
||||
|
|
@ -185,17 +185,17 @@ msgstr ""
|
|||
msgid "Use this form to manage member records and their properties."
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/show.ex:24
|
||||
#: lib/mv_web/live/member_live/show.ex:25
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Id"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/show.ex:30
|
||||
#: lib/mv_web/live/member_live/show.ex:31
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "No"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/show.ex:80
|
||||
#: lib/mv_web/live/member_live/show.ex:94
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Show Member"
|
||||
msgstr ""
|
||||
|
|
@ -205,7 +205,7 @@ msgstr ""
|
|||
msgid "This is a member record from your database."
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/show.ex:30
|
||||
#: lib/mv_web/live/member_live/show.ex:31
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Yes"
|
||||
msgstr ""
|
||||
|
|
@ -282,17 +282,17 @@ msgstr ""
|
|||
msgid "Description"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/user_live/show.ex:17
|
||||
#: lib/mv_web/live/user_live/show.ex:18
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Edit User"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/user_live/show.ex:27
|
||||
#: lib/mv_web/live/user_live/show.ex:28
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Enabled"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/user_live/show.ex:23
|
||||
#: lib/mv_web/live/user_live/show.ex:24
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "ID"
|
||||
msgstr ""
|
||||
|
|
@ -302,7 +302,7 @@ msgstr ""
|
|||
msgid "Immutable"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/components/layouts/navbar.ex:73
|
||||
#: lib/mv_web/components/layouts/navbar.ex:94
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Logout"
|
||||
msgstr ""
|
||||
|
|
@ -318,14 +318,14 @@ msgstr ""
|
|||
msgid "Member"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/components/layouts/navbar.ex:14
|
||||
#: lib/mv_web/live/member_live/index.ex:12
|
||||
#: lib/mv_web/components/layouts/navbar.ex:19
|
||||
#: lib/mv_web/live/member_live/index.ex:14
|
||||
#: lib/mv_web/live/member_live/index.html.heex:3
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Members"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/index.html.heex:50
|
||||
#: lib/mv_web/live/member_live/index.html.heex:57
|
||||
#: lib/mv_web/live/property_type_live/form.ex:16
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Name"
|
||||
|
|
@ -336,12 +336,12 @@ msgstr ""
|
|||
msgid "New User"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/user_live/show.ex:27
|
||||
#: lib/mv_web/live/user_live/show.ex:28
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Not enabled"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/user_live/show.ex:25
|
||||
#: lib/mv_web/live/user_live/show.ex:26
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Not set"
|
||||
msgstr ""
|
||||
|
|
@ -353,12 +353,12 @@ msgid "Note"
|
|||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/user_live/index.html.heex:52
|
||||
#: lib/mv_web/live/user_live/show.ex:25
|
||||
#: lib/mv_web/live/user_live/show.ex:26
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "OIDC ID"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/user_live/show.ex:26
|
||||
#: lib/mv_web/live/user_live/show.ex:27
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Password Authentication"
|
||||
msgstr ""
|
||||
|
|
@ -368,7 +368,7 @@ msgstr ""
|
|||
msgid "Please select a property type first"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/components/layouts/navbar.ex:69
|
||||
#: lib/mv_web/components/layouts/navbar.ex:89
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Profil"
|
||||
msgstr ""
|
||||
|
|
@ -403,17 +403,17 @@ msgstr ""
|
|||
msgid "Save Property type"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/index.html.heex:27
|
||||
#: lib/mv_web/live/member_live/index.html.heex:34
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Select all members"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/index.html.heex:41
|
||||
#: lib/mv_web/live/member_live/index.html.heex:48
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Select member"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/components/layouts/navbar.ex:72
|
||||
#: lib/mv_web/components/layouts/navbar.ex:92
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Settings"
|
||||
msgstr ""
|
||||
|
|
@ -423,7 +423,7 @@ msgstr ""
|
|||
msgid "Save User"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/user_live/show.ex:38
|
||||
#: lib/mv_web/live/user_live/show.ex:54
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Show User"
|
||||
msgstr ""
|
||||
|
|
@ -554,7 +554,57 @@ msgstr ""
|
|||
msgid "User will be created without a password. Check 'Set Password' to add one."
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/auth_overrides.ex:30
|
||||
#: lib/mv_web/live/user_live/show.ex:30
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "or"
|
||||
msgid "Linked Member"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/show.ex:41
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Linked User"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/user_live/show.ex:40
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "No member linked"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/show.ex:51
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "No user linked"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/show.ex:14
|
||||
#: lib/mv_web/live/member_live/show.ex:16
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Back to members list"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/user_live/show.ex:13
|
||||
#: lib/mv_web/live/user_live/show.ex:15
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Back to users list"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/components/layouts/navbar.ex:27
|
||||
#: lib/mv_web/components/layouts/navbar.ex:33
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Select language"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/components/layouts/navbar.ex:40
|
||||
#: lib/mv_web/components/layouts/navbar.ex:60
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Toggle dark mode"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/components/search_bar_component.ex:15
|
||||
#: lib/mv_web/live/member_live/index.html.heex:15
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Search..."
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/components/layouts/navbar.ex:20
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Users"
|
||||
msgstr ""
|
||||
|
|
|
|||
|
|
@ -58,6 +58,3 @@ msgstr ""
|
|||
|
||||
msgid "Your password has successfully been reset"
|
||||
msgstr ""
|
||||
|
||||
msgid "Sign in with Rauthy"
|
||||
msgstr "Sign in with Vereinscloud"
|
||||
|
|
|
|||
|
|
@ -16,69 +16,69 @@ msgstr ""
|
|||
msgid "Actions"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/index.html.heex:77
|
||||
#: lib/mv_web/live/member_live/index.html.heex:84
|
||||
#: lib/mv_web/live/user_live/index.html.heex:65
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Are you sure?"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/components/layouts.ex:71
|
||||
#: lib/mv_web/components/layouts.ex:83
|
||||
#: lib/mv_web/components/layouts.ex:80
|
||||
#: lib/mv_web/components/layouts.ex:92
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Attempting to reconnect"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/form.ex:25
|
||||
#: lib/mv_web/live/member_live/index.html.heex:62
|
||||
#: lib/mv_web/live/member_live/show.ex:36
|
||||
#: lib/mv_web/live/member_live/index.html.heex:69
|
||||
#: lib/mv_web/live/member_live/show.ex:37
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "City"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/index.html.heex:79
|
||||
#: lib/mv_web/live/member_live/index.html.heex:86
|
||||
#: lib/mv_web/live/user_live/index.html.heex:67
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Delete"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/index.html.heex:71
|
||||
#: lib/mv_web/live/member_live/index.html.heex:78
|
||||
#: lib/mv_web/live/user_live/form.ex:109
|
||||
#: lib/mv_web/live/user_live/index.html.heex:59
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Edit"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/show.ex:18
|
||||
#: lib/mv_web/live/member_live/show.ex:81
|
||||
#: lib/mv_web/live/member_live/show.ex:19
|
||||
#: lib/mv_web/live/member_live/show.ex:95
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Edit Member"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/form.ex:18
|
||||
#: lib/mv_web/live/member_live/index.html.heex:58
|
||||
#: lib/mv_web/live/member_live/show.ex:27
|
||||
#: lib/mv_web/live/member_live/index.html.heex:65
|
||||
#: lib/mv_web/live/member_live/show.ex:28
|
||||
#: lib/mv_web/live/user_live/form.ex:14
|
||||
#: lib/mv_web/live/user_live/index.html.heex:44
|
||||
#: lib/mv_web/live/user_live/show.ex:24
|
||||
#: lib/mv_web/live/user_live/show.ex:25
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Email"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/form.ex:16
|
||||
#: lib/mv_web/live/member_live/show.ex:25
|
||||
#: lib/mv_web/live/member_live/show.ex:26
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "First Name"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/form.ex:22
|
||||
#: lib/mv_web/live/member_live/index.html.heex:64
|
||||
#: lib/mv_web/live/member_live/show.ex:33
|
||||
#: lib/mv_web/live/member_live/index.html.heex:71
|
||||
#: lib/mv_web/live/member_live/show.ex:34
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Join Date"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/form.ex:17
|
||||
#: lib/mv_web/live/member_live/show.ex:26
|
||||
#: lib/mv_web/live/member_live/show.ex:27
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Last Name"
|
||||
msgstr ""
|
||||
|
|
@ -88,18 +88,18 @@ msgstr ""
|
|||
msgid "New Member"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/index.html.heex:68
|
||||
#: lib/mv_web/live/member_live/index.html.heex:75
|
||||
#: lib/mv_web/live/user_live/index.html.heex:56
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Show"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/components/layouts.ex:78
|
||||
#: lib/mv_web/components/layouts.ex:87
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Something went wrong!"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/components/layouts.ex:66
|
||||
#: lib/mv_web/components/layouts.ex:75
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "We can't find the internet"
|
||||
msgstr ""
|
||||
|
|
@ -110,52 +110,52 @@ msgid "close"
|
|||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/form.ex:19
|
||||
#: lib/mv_web/live/member_live/show.ex:28
|
||||
#: lib/mv_web/live/member_live/show.ex:29
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Birth Date"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/form.ex:30
|
||||
#: lib/mv_web/live/member_live/show.ex:42
|
||||
#: lib/mv_web/live/member_live/show.ex:56
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Custom Properties"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/form.ex:23
|
||||
#: lib/mv_web/live/member_live/show.ex:34
|
||||
#: lib/mv_web/live/member_live/show.ex:35
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Exit Date"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/form.ex:27
|
||||
#: lib/mv_web/live/member_live/index.html.heex:60
|
||||
#: lib/mv_web/live/member_live/show.ex:38
|
||||
#: lib/mv_web/live/member_live/index.html.heex:67
|
||||
#: lib/mv_web/live/member_live/show.ex:39
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "House Number"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/form.ex:24
|
||||
#: lib/mv_web/live/member_live/show.ex:35
|
||||
#: lib/mv_web/live/member_live/show.ex:36
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Notes"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/form.ex:20
|
||||
#: lib/mv_web/live/member_live/show.ex:29
|
||||
#: lib/mv_web/live/member_live/show.ex:30
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Paid"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/form.ex:21
|
||||
#: lib/mv_web/live/member_live/index.html.heex:63
|
||||
#: lib/mv_web/live/member_live/show.ex:32
|
||||
#: lib/mv_web/live/member_live/index.html.heex:70
|
||||
#: lib/mv_web/live/member_live/show.ex:33
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Phone Number"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/form.ex:28
|
||||
#: lib/mv_web/live/member_live/index.html.heex:61
|
||||
#: lib/mv_web/live/member_live/show.ex:39
|
||||
#: lib/mv_web/live/member_live/index.html.heex:68
|
||||
#: lib/mv_web/live/member_live/show.ex:40
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Postal Code"
|
||||
msgstr ""
|
||||
|
|
@ -174,8 +174,8 @@ msgid "Saving..."
|
|||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/form.ex:26
|
||||
#: lib/mv_web/live/member_live/index.html.heex:59
|
||||
#: lib/mv_web/live/member_live/show.ex:37
|
||||
#: lib/mv_web/live/member_live/index.html.heex:66
|
||||
#: lib/mv_web/live/member_live/show.ex:38
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Street"
|
||||
msgstr ""
|
||||
|
|
@ -185,17 +185,17 @@ msgstr ""
|
|||
msgid "Use this form to manage member records and their properties."
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/show.ex:24
|
||||
#: lib/mv_web/live/member_live/show.ex:25
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Id"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/show.ex:30
|
||||
#: lib/mv_web/live/member_live/show.ex:31
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "No"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/show.ex:80
|
||||
#: lib/mv_web/live/member_live/show.ex:94
|
||||
#, elixir-autogen, elixir-format, fuzzy
|
||||
msgid "Show Member"
|
||||
msgstr ""
|
||||
|
|
@ -205,7 +205,7 @@ msgstr ""
|
|||
msgid "This is a member record from your database."
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/show.ex:30
|
||||
#: lib/mv_web/live/member_live/show.ex:31
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Yes"
|
||||
msgstr ""
|
||||
|
|
@ -282,17 +282,17 @@ msgstr ""
|
|||
msgid "Description"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/user_live/show.ex:17
|
||||
#: lib/mv_web/live/user_live/show.ex:18
|
||||
#, elixir-autogen, elixir-format, fuzzy
|
||||
msgid "Edit User"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/user_live/show.ex:27
|
||||
#: lib/mv_web/live/user_live/show.ex:28
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Enabled"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/user_live/show.ex:23
|
||||
#: lib/mv_web/live/user_live/show.ex:24
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "ID"
|
||||
msgstr ""
|
||||
|
|
@ -302,7 +302,7 @@ msgstr ""
|
|||
msgid "Immutable"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/components/layouts/navbar.ex:73
|
||||
#: lib/mv_web/components/layouts/navbar.ex:94
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Logout"
|
||||
msgstr ""
|
||||
|
|
@ -318,14 +318,14 @@ msgstr ""
|
|||
msgid "Member"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/components/layouts/navbar.ex:14
|
||||
#: lib/mv_web/live/member_live/index.ex:12
|
||||
#: lib/mv_web/components/layouts/navbar.ex:19
|
||||
#: lib/mv_web/live/member_live/index.ex:14
|
||||
#: lib/mv_web/live/member_live/index.html.heex:3
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Members"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/index.html.heex:50
|
||||
#: lib/mv_web/live/member_live/index.html.heex:57
|
||||
#: lib/mv_web/live/property_type_live/form.ex:16
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Name"
|
||||
|
|
@ -336,12 +336,12 @@ msgstr ""
|
|||
msgid "New User"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/user_live/show.ex:27
|
||||
#: lib/mv_web/live/user_live/show.ex:28
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Not enabled"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/user_live/show.ex:25
|
||||
#: lib/mv_web/live/user_live/show.ex:26
|
||||
#, elixir-autogen, elixir-format, fuzzy
|
||||
msgid "Not set"
|
||||
msgstr ""
|
||||
|
|
@ -353,12 +353,12 @@ msgid "Note"
|
|||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/user_live/index.html.heex:52
|
||||
#: lib/mv_web/live/user_live/show.ex:25
|
||||
#: lib/mv_web/live/user_live/show.ex:26
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "OIDC ID"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/user_live/show.ex:26
|
||||
#: lib/mv_web/live/user_live/show.ex:27
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Password Authentication"
|
||||
msgstr ""
|
||||
|
|
@ -368,7 +368,7 @@ msgstr ""
|
|||
msgid "Please select a property type first"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/components/layouts/navbar.ex:69
|
||||
#: lib/mv_web/components/layouts/navbar.ex:89
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Profil"
|
||||
msgstr ""
|
||||
|
|
@ -403,17 +403,17 @@ msgstr ""
|
|||
msgid "Save Property type"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/index.html.heex:27
|
||||
#: lib/mv_web/live/member_live/index.html.heex:34
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Select all members"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/index.html.heex:41
|
||||
#: lib/mv_web/live/member_live/index.html.heex:48
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Select member"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/components/layouts/navbar.ex:72
|
||||
#: lib/mv_web/components/layouts/navbar.ex:92
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Settings"
|
||||
msgstr ""
|
||||
|
|
@ -423,7 +423,7 @@ msgstr ""
|
|||
msgid "Save User"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/user_live/show.ex:38
|
||||
#: lib/mv_web/live/user_live/show.ex:54
|
||||
#, elixir-autogen, elixir-format, fuzzy
|
||||
msgid "Show User"
|
||||
msgstr ""
|
||||
|
|
@ -554,7 +554,57 @@ msgstr "Set Password"
|
|||
msgid "User will be created without a password. Check 'Set Password' to add one."
|
||||
msgstr "User will be created without a password. Check 'Set Password' to add one."
|
||||
|
||||
#: lib/mv_web/auth_overrides.ex:30
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "or"
|
||||
#: lib/mv_web/live/user_live/show.ex:30
|
||||
#, elixir-autogen, elixir-format, fuzzy
|
||||
msgid "Linked Member"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/show.ex:41
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Linked User"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/user_live/show.ex:40
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "No member linked"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/show.ex:51
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "No user linked"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/member_live/show.ex:14
|
||||
#: lib/mv_web/live/member_live/show.ex:16
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Back to members list"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/user_live/show.ex:13
|
||||
#: lib/mv_web/live/user_live/show.ex:15
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Back to users list"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/components/layouts/navbar.ex:27
|
||||
#: lib/mv_web/components/layouts/navbar.ex:33
|
||||
#, elixir-autogen, elixir-format, fuzzy
|
||||
msgid "Select language"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/components/layouts/navbar.ex:40
|
||||
#: lib/mv_web/components/layouts/navbar.ex:60
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Toggle dark mode"
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/live/components/search_bar_component.ex:15
|
||||
#: lib/mv_web/live/member_live/index.html.heex:15
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Search..."
|
||||
msgstr ""
|
||||
|
||||
#: lib/mv_web/components/layouts/navbar.ex:20
|
||||
#, elixir-autogen, elixir-format, fuzzy
|
||||
msgid "Users"
|
||||
msgstr ""
|
||||
|
|
|
|||
|
|
@ -110,3 +110,48 @@ msgstr ""
|
|||
|
||||
msgid "must be equal to %{number}"
|
||||
msgstr ""
|
||||
|
||||
## Ash Framework - Standard constraint messages
|
||||
msgid "length must be greater than or equal to %{min}"
|
||||
msgstr ""
|
||||
|
||||
msgid "length must be less than or equal to %{max}"
|
||||
msgstr ""
|
||||
|
||||
msgid "must be present"
|
||||
msgstr ""
|
||||
|
||||
## Custom validation messages from Mv.Accounts.User
|
||||
msgid "User already has a member. Remove existing member first."
|
||||
msgstr ""
|
||||
|
||||
msgid "OIDC user_info must contain a non-empty 'sub' or 'id' field"
|
||||
msgstr ""
|
||||
|
||||
## Custom validation messages from Mv.Membership.Member
|
||||
msgid "User is already linked to another member"
|
||||
msgstr ""
|
||||
|
||||
msgid "User not found"
|
||||
msgstr ""
|
||||
|
||||
msgid "cannot be in the future"
|
||||
msgstr ""
|
||||
|
||||
msgid "cannot be before join date"
|
||||
msgstr ""
|
||||
|
||||
msgid "is not a valid phone number"
|
||||
msgstr ""
|
||||
|
||||
msgid "must consist of 5 digits"
|
||||
msgstr ""
|
||||
|
||||
msgid "is not a valid email"
|
||||
msgstr ""
|
||||
|
||||
msgid "must have length of at least 8"
|
||||
msgstr ""
|
||||
|
||||
msgid "is required"
|
||||
msgstr ""
|
||||
|
|
|
|||
|
|
@ -107,3 +107,48 @@ msgstr ""
|
|||
|
||||
msgid "must be equal to %{number}"
|
||||
msgstr ""
|
||||
|
||||
## Ash Framework - Standard constraint messages
|
||||
msgid "length must be greater than or equal to %{min}"
|
||||
msgstr ""
|
||||
|
||||
msgid "length must be less than or equal to %{max}"
|
||||
msgstr ""
|
||||
|
||||
msgid "must be present"
|
||||
msgstr ""
|
||||
|
||||
## Custom validation messages from Mv.Accounts.User
|
||||
msgid "User already has a member. Remove existing member first."
|
||||
msgstr ""
|
||||
|
||||
msgid "OIDC user_info must contain a non-empty 'sub' or 'id' field"
|
||||
msgstr ""
|
||||
|
||||
## Custom validation messages from Mv.Membership.Member
|
||||
msgid "User is already linked to another member"
|
||||
msgstr ""
|
||||
|
||||
msgid "User not found"
|
||||
msgstr ""
|
||||
|
||||
msgid "cannot be in the future"
|
||||
msgstr ""
|
||||
|
||||
msgid "cannot be before join date"
|
||||
msgstr ""
|
||||
|
||||
msgid "is not a valid phone number"
|
||||
msgstr ""
|
||||
|
||||
msgid "must consist of 5 digits"
|
||||
msgstr ""
|
||||
|
||||
msgid "is not a valid email"
|
||||
msgstr ""
|
||||
|
||||
msgid "must have length of at least 8"
|
||||
msgstr ""
|
||||
|
||||
msgid "is required"
|
||||
msgstr ""
|
||||
|
|
|
|||
19
priv/repo/migrations/20250926164519_member_relation.exs
Normal file
19
priv/repo/migrations/20250926164519_member_relation.exs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
defmodule Mv.Repo.Migrations.MemberRelation do
|
||||
@moduledoc """
|
||||
Updates resources based on their most recent snapshots.
|
||||
|
||||
This file was autogenerated with `mix ash_postgres.generate_migrations`
|
||||
"""
|
||||
|
||||
use Ecto.Migration
|
||||
|
||||
def up do
|
||||
# Ensure 1:1 relationship - one user can only be linked to one member
|
||||
# This prevents multiple users from sharing the same member account
|
||||
create unique_index(:users, [:member_id], name: "users_unique_member_index")
|
||||
end
|
||||
|
||||
def down do
|
||||
drop_if_exists unique_index(:users, [:member_id], name: "users_unique_member_index")
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
defmodule Mv.Repo.Migrations.AddUniqueEmailToMembers do
|
||||
@moduledoc """
|
||||
Updates resources based on their most recent snapshots.
|
||||
|
||||
This file was autogenerated with `mix ash_postgres.generate_migrations`
|
||||
"""
|
||||
|
||||
use Ecto.Migration
|
||||
|
||||
def up do
|
||||
# Ensure email uniqueness across all members
|
||||
# This supports upsert operations and prevents duplicate member accounts
|
||||
create unique_index(:members, [:email], name: "members_unique_email_index")
|
||||
end
|
||||
|
||||
def down do
|
||||
drop_if_exists unique_index(:members, [:email], name: "members_unique_email_index")
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
defmodule Mv.Repo.Migrations.AddConstraintsForUserMemberAndProperty do
|
||||
@moduledoc """
|
||||
Updates resources based on their most recent snapshots.
|
||||
|
||||
This file was autogenerated with `mix ash_postgres.generate_migrations`
|
||||
"""
|
||||
|
||||
use Ecto.Migration
|
||||
|
||||
def up do
|
||||
drop constraint(:users, "users_member_id_fkey")
|
||||
|
||||
alter table(:users) do
|
||||
modify :member_id,
|
||||
references(:members,
|
||||
column: :id,
|
||||
name: "users_member_id_fkey",
|
||||
type: :uuid,
|
||||
prefix: "public",
|
||||
on_delete: :nilify_all
|
||||
)
|
||||
end
|
||||
|
||||
create unique_index(:properties, [:member_id, :property_type_id],
|
||||
name: "properties_unique_property_per_member_index"
|
||||
)
|
||||
end
|
||||
|
||||
def down do
|
||||
drop_if_exists unique_index(:properties, [:member_id, :property_type_id],
|
||||
name: "properties_unique_property_per_member_index"
|
||||
)
|
||||
|
||||
drop constraint(:users, "users_member_id_fkey")
|
||||
|
||||
alter table(:users) do
|
||||
modify :member_id,
|
||||
references(:members,
|
||||
column: :id,
|
||||
name: "users_member_id_fkey",
|
||||
type: :uuid,
|
||||
prefix: "public"
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -48,7 +48,7 @@ Accounts.create_user!(%{email: "admin@mv.local"}, upsert?: true, upsert_identity
|
|||
|> Ash.Changeset.for_update(:admin_set_password, %{password: "testpassword"})
|
||||
|> Ash.update!()
|
||||
|
||||
# Create sample members for testing
|
||||
# Create sample members for testing - use upsert to prevent duplicates
|
||||
for member_attrs <- [
|
||||
%{
|
||||
first_name: "Hans",
|
||||
|
|
@ -90,5 +90,96 @@ for member_attrs <- [
|
|||
house_number: "8"
|
||||
}
|
||||
] do
|
||||
Membership.create_member!(member_attrs)
|
||||
# Use upsert to prevent duplicates based on email
|
||||
Membership.create_member!(member_attrs, upsert?: true, upsert_identity: :unique_email)
|
||||
end
|
||||
|
||||
# Create additional users for user-member linking examples
|
||||
additional_users = [
|
||||
%{email: "hans.mueller@example.de"},
|
||||
%{email: "greta.schmidt@example.de"},
|
||||
%{email: "maria.weber@example.de"},
|
||||
%{email: "thomas.klein@example.de"}
|
||||
]
|
||||
|
||||
created_users =
|
||||
Enum.map(additional_users, fn user_attrs ->
|
||||
Accounts.create_user!(user_attrs, upsert?: true, upsert_identity: :unique_email)
|
||||
|> Ash.Changeset.for_update(:admin_set_password, %{password: "testpassword"})
|
||||
|> Ash.update!()
|
||||
end)
|
||||
|
||||
# Create members with linked users to demonstrate the 1:1 relationship
|
||||
# Only create if users don't already have members
|
||||
linked_members = [
|
||||
%{
|
||||
first_name: "Maria",
|
||||
last_name: "Weber",
|
||||
email: "maria.weber@example.de",
|
||||
birth_date: ~D[1992-07-14],
|
||||
join_date: ~D[2023-03-15],
|
||||
paid: true,
|
||||
phone_number: "+49301357924",
|
||||
city: "Frankfurt",
|
||||
street: "Goetheplatz",
|
||||
house_number: "5",
|
||||
postal_code: "60313",
|
||||
notes: "Linked to user account",
|
||||
# Link to the third user (maria.weber@example.de)
|
||||
user: Enum.at(created_users, 2)
|
||||
},
|
||||
%{
|
||||
first_name: "Thomas",
|
||||
last_name: "Klein",
|
||||
email: "thomas.klein@example.de",
|
||||
birth_date: ~D[1988-12-03],
|
||||
join_date: ~D[2023-04-01],
|
||||
paid: false,
|
||||
phone_number: "+49302468135",
|
||||
city: "Köln",
|
||||
street: "Rheinstraße",
|
||||
house_number: "23",
|
||||
postal_code: "50667",
|
||||
notes: "Linked to user account - needs payment follow-up",
|
||||
# Link to the fourth user (thomas.klein@example.de)
|
||||
user: Enum.at(created_users, 3)
|
||||
}
|
||||
]
|
||||
|
||||
# Create the linked members - use upsert to prevent duplicates
|
||||
Enum.each(linked_members, fn member_attrs ->
|
||||
user = member_attrs.user
|
||||
member_attrs_without_user = Map.delete(member_attrs, :user)
|
||||
|
||||
# Check if user already has a member
|
||||
if user.member_id == nil do
|
||||
# User is free, create member and link - use upsert to prevent duplicates
|
||||
Membership.create_member!(
|
||||
Map.put(member_attrs_without_user, :user, %{id: user.id}),
|
||||
upsert?: true,
|
||||
upsert_identity: :unique_email
|
||||
)
|
||||
else
|
||||
# User already has a member, just create the member without linking - use upsert to prevent duplicates
|
||||
Membership.create_member!(member_attrs_without_user,
|
||||
upsert?: true,
|
||||
upsert_identity: :unique_email
|
||||
)
|
||||
end
|
||||
end)
|
||||
|
||||
IO.puts("✅ Seeds completed successfully!")
|
||||
IO.puts("📝 Created sample data:")
|
||||
IO.puts(" - Property types: String, Date, Boolean, Email")
|
||||
IO.puts(" - Admin user: admin@mv.local (password: testpassword)")
|
||||
IO.puts(" - Sample members: Hans, Greta, Friedrich")
|
||||
|
||||
IO.puts(
|
||||
" - Additional users: hans.mueller@example.de, greta.schmidt@example.de, maria.weber@example.de, thomas.klein@example.de"
|
||||
)
|
||||
|
||||
IO.puts(
|
||||
" - Linked members: Maria Weber ↔ maria.weber@example.de, Thomas Klein ↔ thomas.klein@example.de"
|
||||
)
|
||||
|
||||
IO.puts("🔗 Visit the application to see user-member relationships in action!")
|
||||
|
|
|
|||
202
priv/resource_snapshots/repo/members/20250926180341.json
Normal file
202
priv/resource_snapshots/repo/members/20250926180341.json
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"uuid_generate_v7()\")",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "first_name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "last_name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "email",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "birth_date",
|
||||
"type": "date"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "paid",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "phone_number",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "join_date",
|
||||
"type": "date"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "exit_date",
|
||||
"type": "date"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "notes",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "city",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "street",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "house_number",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "postal_code",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "5F070A1E5BEE9883AE864FB5A4A5E81F487A1C57D41576C23BAC8D933005D565",
|
||||
"identities": [
|
||||
{
|
||||
"all_tenants?": false,
|
||||
"base_filter": null,
|
||||
"index_name": "members_unique_email_index",
|
||||
"keys": [
|
||||
{
|
||||
"type": "atom",
|
||||
"value": "email"
|
||||
}
|
||||
],
|
||||
"name": "unique_email",
|
||||
"nils_distinct?": true,
|
||||
"where": null
|
||||
}
|
||||
],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.Mv.Repo",
|
||||
"schema": null,
|
||||
"table": "members"
|
||||
}
|
||||
214
priv/resource_snapshots/repo/members/20251016130855.json
Normal file
214
priv/resource_snapshots/repo/members/20251016130855.json
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"uuid_generate_v7()\")",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "first_name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "last_name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "email",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "birth_date",
|
||||
"type": "date"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "paid",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "phone_number",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "join_date",
|
||||
"type": "date"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "exit_date",
|
||||
"type": "date"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "notes",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "city",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "street",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "house_number",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "postal_code",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "search_vector",
|
||||
"type": "tsvector"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "6AAC71BCCA5F112087CEF6877A5BBF74EF8965D5DA4812C44CD6E672F882CC3F",
|
||||
"identities": [
|
||||
{
|
||||
"all_tenants?": false,
|
||||
"base_filter": null,
|
||||
"index_name": "members_unique_email_index",
|
||||
"keys": [
|
||||
{
|
||||
"type": "atom",
|
||||
"value": "email"
|
||||
}
|
||||
],
|
||||
"name": "unique_email",
|
||||
"nils_distinct?": true,
|
||||
"where": null
|
||||
}
|
||||
],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.Mv.Repo",
|
||||
"schema": null,
|
||||
"table": "members"
|
||||
}
|
||||
124
priv/resource_snapshots/repo/properties/20251016130855.json
Normal file
124
priv/resource_snapshots/repo/properties/20251016130855.json
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"gen_random_uuid()\")",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "value",
|
||||
"type": "map"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": {
|
||||
"deferrable": false,
|
||||
"destination_attribute": "id",
|
||||
"destination_attribute_default": null,
|
||||
"destination_attribute_generated": null,
|
||||
"index?": false,
|
||||
"match_type": null,
|
||||
"match_with": null,
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"name": "properties_member_id_fkey",
|
||||
"on_delete": "delete",
|
||||
"on_update": null,
|
||||
"primary_key?": true,
|
||||
"schema": "public",
|
||||
"table": "members"
|
||||
},
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "member_id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": {
|
||||
"deferrable": false,
|
||||
"destination_attribute": "id",
|
||||
"destination_attribute_default": null,
|
||||
"destination_attribute_generated": null,
|
||||
"index?": false,
|
||||
"match_type": null,
|
||||
"match_with": null,
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"name": "properties_property_type_id_fkey",
|
||||
"on_delete": null,
|
||||
"on_update": null,
|
||||
"primary_key?": true,
|
||||
"schema": "public",
|
||||
"table": "property_types"
|
||||
},
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "property_type_id",
|
||||
"type": "uuid"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "8F90B1AAD1063CF2BB0BDEBBDFBA86AF0B24D854689FB834BC20DFAB2143A451",
|
||||
"identities": [
|
||||
{
|
||||
"all_tenants?": false,
|
||||
"base_filter": null,
|
||||
"index_name": "properties_unique_property_per_member_index",
|
||||
"keys": [
|
||||
{
|
||||
"type": "atom",
|
||||
"value": "member_id"
|
||||
},
|
||||
{
|
||||
"type": "atom",
|
||||
"value": "property_type_id"
|
||||
}
|
||||
],
|
||||
"name": "unique_property_per_member",
|
||||
"nils_distinct?": true,
|
||||
"where": null
|
||||
}
|
||||
],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.Mv.Repo",
|
||||
"schema": null,
|
||||
"table": "properties"
|
||||
}
|
||||
141
priv/resource_snapshots/repo/users/20250926164519.json
Normal file
141
priv/resource_snapshots/repo/users/20250926164519.json
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"gen_random_uuid()\")",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "email",
|
||||
"type": "citext"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "hashed_password",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "oidc_id",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": {
|
||||
"deferrable": false,
|
||||
"destination_attribute": "id",
|
||||
"destination_attribute_default": null,
|
||||
"destination_attribute_generated": null,
|
||||
"index?": false,
|
||||
"match_type": null,
|
||||
"match_with": null,
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"name": "users_member_id_fkey",
|
||||
"on_delete": null,
|
||||
"on_update": null,
|
||||
"primary_key?": true,
|
||||
"schema": "public",
|
||||
"table": "members"
|
||||
},
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "member_id",
|
||||
"type": "uuid"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "FDEBD4840449609DDA8B50D6741C2EEDE9D81DFBC1E26D4BC77DBD9B5A8EA4DC",
|
||||
"identities": [
|
||||
{
|
||||
"all_tenants?": false,
|
||||
"base_filter": null,
|
||||
"index_name": "users_unique_email_index",
|
||||
"keys": [
|
||||
{
|
||||
"type": "atom",
|
||||
"value": "email"
|
||||
}
|
||||
],
|
||||
"name": "unique_email",
|
||||
"nils_distinct?": true,
|
||||
"where": null
|
||||
},
|
||||
{
|
||||
"all_tenants?": false,
|
||||
"base_filter": null,
|
||||
"index_name": "users_unique_oidc_id_index",
|
||||
"keys": [
|
||||
{
|
||||
"type": "atom",
|
||||
"value": "oidc_id"
|
||||
}
|
||||
],
|
||||
"name": "unique_oidc_id",
|
||||
"nils_distinct?": true,
|
||||
"where": null
|
||||
},
|
||||
{
|
||||
"all_tenants?": false,
|
||||
"base_filter": null,
|
||||
"index_name": "users_unique_member_index",
|
||||
"keys": [
|
||||
{
|
||||
"type": "atom",
|
||||
"value": "member_id"
|
||||
}
|
||||
],
|
||||
"name": "unique_member",
|
||||
"nils_distinct?": true,
|
||||
"where": null
|
||||
}
|
||||
],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.Mv.Repo",
|
||||
"schema": null,
|
||||
"table": "users"
|
||||
}
|
||||
141
priv/resource_snapshots/repo/users/20251016130855.json
Normal file
141
priv/resource_snapshots/repo/users/20251016130855.json
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"gen_random_uuid()\")",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "email",
|
||||
"type": "citext"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "hashed_password",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "oidc_id",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": {
|
||||
"deferrable": false,
|
||||
"destination_attribute": "id",
|
||||
"destination_attribute_default": null,
|
||||
"destination_attribute_generated": null,
|
||||
"index?": false,
|
||||
"match_type": null,
|
||||
"match_with": null,
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"name": "users_member_id_fkey",
|
||||
"on_delete": "nilify",
|
||||
"on_update": null,
|
||||
"primary_key?": true,
|
||||
"schema": "public",
|
||||
"table": "members"
|
||||
},
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "member_id",
|
||||
"type": "uuid"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "1D22936DF847949B543000F3E2E4BDA7D78682AAE6EE0CB9CBD55A4F8F4A7228",
|
||||
"identities": [
|
||||
{
|
||||
"all_tenants?": false,
|
||||
"base_filter": null,
|
||||
"index_name": "users_unique_email_index",
|
||||
"keys": [
|
||||
{
|
||||
"type": "atom",
|
||||
"value": "email"
|
||||
}
|
||||
],
|
||||
"name": "unique_email",
|
||||
"nils_distinct?": true,
|
||||
"where": null
|
||||
},
|
||||
{
|
||||
"all_tenants?": false,
|
||||
"base_filter": null,
|
||||
"index_name": "users_unique_member_index",
|
||||
"keys": [
|
||||
{
|
||||
"type": "atom",
|
||||
"value": "member_id"
|
||||
}
|
||||
],
|
||||
"name": "unique_member",
|
||||
"nils_distinct?": true,
|
||||
"where": null
|
||||
},
|
||||
{
|
||||
"all_tenants?": false,
|
||||
"base_filter": null,
|
||||
"index_name": "users_unique_oidc_id_index",
|
||||
"keys": [
|
||||
{
|
||||
"type": "atom",
|
||||
"value": "oidc_id"
|
||||
}
|
||||
],
|
||||
"name": "unique_oidc_id",
|
||||
"nils_distinct?": true,
|
||||
"where": null
|
||||
}
|
||||
],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.Mv.Repo",
|
||||
"schema": null,
|
||||
"table": "users"
|
||||
}
|
||||
88
test/accounts/user_member_deletion_test.exs
Normal file
88
test/accounts/user_member_deletion_test.exs
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
defmodule Mv.Accounts.UserMemberDeletionTest do
|
||||
@moduledoc """
|
||||
Tests for ON DELETE SET NULL constraint on users.member_id.
|
||||
When a member is deleted, the linked user should remain but with member_id set to NULL.
|
||||
"""
|
||||
use Mv.DataCase, async: true
|
||||
alias Mv.Accounts
|
||||
alias Mv.Membership
|
||||
|
||||
describe "User remains when linked Member is deleted (ON DELETE SET NULL)" do
|
||||
@valid_user_attrs %{
|
||||
email: "test@example.com"
|
||||
}
|
||||
|
||||
@valid_member_attrs %{
|
||||
first_name: "John",
|
||||
last_name: "Doe",
|
||||
email: "john@example.com"
|
||||
}
|
||||
|
||||
test "deleting a member sets the user's member_id to NULL" do
|
||||
# Create a member
|
||||
{:ok, member} = Membership.create_member(@valid_member_attrs)
|
||||
|
||||
# Create a user linked to the member
|
||||
{:ok, user} =
|
||||
Accounts.create_user(Map.put(@valid_user_attrs, :member, %{id: member.id}))
|
||||
|
||||
# Verify the relationship is established
|
||||
{:ok, user_before_delete} = Ash.get(Mv.Accounts.User, user.id, load: [:member])
|
||||
assert user_before_delete.member_id == member.id
|
||||
assert user_before_delete.member.id == member.id
|
||||
|
||||
# Delete the member
|
||||
:ok = Membership.destroy_member(member)
|
||||
|
||||
# Verify the user still exists but member_id is NULL
|
||||
{:ok, user_after_delete} = Ash.get(Mv.Accounts.User, user.id, load: [:member])
|
||||
assert user_after_delete.id == user.id
|
||||
assert user_after_delete.member_id == nil
|
||||
assert user_after_delete.member == nil
|
||||
end
|
||||
|
||||
test "user can be linked to a new member after old member is deleted" do
|
||||
# Create first member
|
||||
{:ok, member1} = Membership.create_member(@valid_member_attrs)
|
||||
|
||||
# Create user linked to first member
|
||||
{:ok, user} =
|
||||
Accounts.create_user(Map.put(@valid_user_attrs, :member, %{id: member1.id}))
|
||||
|
||||
assert user.member_id == member1.id
|
||||
|
||||
# Delete first member
|
||||
:ok = Membership.destroy_member(member1)
|
||||
|
||||
# Reload user from database to get updated member_id (should be NULL)
|
||||
{:ok, user_after_delete} = Ash.get(Mv.Accounts.User, user.id)
|
||||
assert user_after_delete.member_id == nil
|
||||
|
||||
# Create second member
|
||||
{:ok, member2} =
|
||||
Membership.create_member(%{
|
||||
first_name: "Jane",
|
||||
last_name: "Smith",
|
||||
email: "jane@example.com"
|
||||
})
|
||||
|
||||
# Link user to second member (use reloaded user)
|
||||
{:ok, updated_user} = Accounts.update_user(user_after_delete, %{member: %{id: member2.id}})
|
||||
|
||||
# Verify new relationship
|
||||
{:ok, final_user} = Ash.get(Mv.Accounts.User, updated_user.id, load: [:member])
|
||||
assert final_user.member_id == member2.id
|
||||
assert final_user.member.id == member2.id
|
||||
end
|
||||
|
||||
test "member without linked user can be deleted normally" do
|
||||
{:ok, member} = Membership.create_member(@valid_member_attrs)
|
||||
|
||||
# Delete member (no users linked)
|
||||
assert :ok = Membership.destroy_member(member)
|
||||
|
||||
# Verify member is deleted
|
||||
assert {:error, _} = Ash.get(Mv.Membership.Member, member.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
197
test/accounts/user_member_relationship_test.exs
Normal file
197
test/accounts/user_member_relationship_test.exs
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
defmodule Mv.Accounts.UserMemberRelationshipTest do
|
||||
# Using async: true for faster test execution
|
||||
# This is safe because all database operations are sandboxed per test
|
||||
use Mv.DataCase, async: true
|
||||
alias Mv.Accounts
|
||||
alias Mv.Membership
|
||||
|
||||
describe "User-Member Relationship - Basic Tests" do
|
||||
@valid_user_attrs %{
|
||||
email: "test@example.com"
|
||||
}
|
||||
|
||||
@valid_member_attrs %{
|
||||
first_name: "John",
|
||||
last_name: "Doe",
|
||||
email: "john@example.com"
|
||||
}
|
||||
|
||||
test "user can exist without member" do
|
||||
{:ok, user} = Accounts.create_user(@valid_user_attrs)
|
||||
assert user.member_id == nil
|
||||
|
||||
# Load the relationship to test it
|
||||
{:ok, user_with_member} = Ash.get(Mv.Accounts.User, user.id, load: [:member])
|
||||
assert user_with_member.member == nil
|
||||
end
|
||||
|
||||
test "member can exist without user" do
|
||||
{:ok, member} = Membership.create_member(@valid_member_attrs)
|
||||
assert member.id != nil
|
||||
assert member.first_name == "John"
|
||||
|
||||
# Load the relationship to test it
|
||||
{:ok, member_with_user} = Ash.get(Mv.Membership.Member, member.id, load: [:user])
|
||||
assert member_with_user.user == nil
|
||||
end
|
||||
end
|
||||
|
||||
describe "User-Member Relationship - Linking Tests" do
|
||||
@valid_user_attrs %{
|
||||
email: "test1@example.com"
|
||||
}
|
||||
|
||||
@valid_member_attrs %{
|
||||
first_name: "Alice",
|
||||
last_name: "Johnson",
|
||||
email: "alice@example.com"
|
||||
}
|
||||
|
||||
test "user can be linked to member during user creation" do
|
||||
{:ok, member} = Membership.create_member(@valid_member_attrs)
|
||||
|
||||
user_attrs = Map.put(@valid_user_attrs, :member, %{id: member.id})
|
||||
{:ok, user} = Accounts.create_user(user_attrs)
|
||||
|
||||
# Load the relationship to test it
|
||||
{:ok, user_with_member} = Ash.get(Mv.Accounts.User, user.id, load: [:member])
|
||||
assert user_with_member.member.id == member.id
|
||||
end
|
||||
|
||||
test "member can be linked to user during member creation using manage_relationship" do
|
||||
{:ok, user} = Accounts.create_user(@valid_user_attrs)
|
||||
|
||||
member_attrs = Map.put(@valid_member_attrs, :user, %{id: user.id})
|
||||
{:ok, member} = Membership.create_member(member_attrs)
|
||||
|
||||
# Load the relationship to test it
|
||||
{:ok, member_with_user} = Ash.get(Mv.Membership.Member, member.id, load: [:user])
|
||||
assert member_with_user.user.id == user.id
|
||||
end
|
||||
|
||||
test "user can be linked to member during update" do
|
||||
{:ok, user} = Accounts.create_user(@valid_user_attrs)
|
||||
{:ok, member} = Membership.create_member(@valid_member_attrs)
|
||||
|
||||
{:ok, updated_user} = Accounts.update_user(user, %{member: %{id: member.id}})
|
||||
|
||||
# Load the relationship to test it
|
||||
{:ok, user_with_member} = Ash.get(Mv.Accounts.User, updated_user.id, load: [:member])
|
||||
assert user_with_member.member.id == member.id
|
||||
end
|
||||
|
||||
test "member can be linked to user during update using manage_relationship" do
|
||||
{:ok, user} = Accounts.create_user(@valid_user_attrs)
|
||||
{:ok, member} = Membership.create_member(@valid_member_attrs)
|
||||
|
||||
{:ok, _updated_member} = Membership.update_member(member, %{user: %{id: user.id}})
|
||||
|
||||
# Load the relationship to test it
|
||||
{:ok, member_with_user} = Ash.get(Mv.Membership.Member, member.id, load: [:user])
|
||||
assert member_with_user.user.id == user.id
|
||||
end
|
||||
end
|
||||
|
||||
describe "User-Member Relationship - Inverse Relationship Tests" do
|
||||
@valid_user_attrs %{
|
||||
email: "test2@example.com"
|
||||
}
|
||||
|
||||
@valid_member_attrs %{
|
||||
first_name: "Bob",
|
||||
last_name: "Smith",
|
||||
email: "bob@example.com"
|
||||
}
|
||||
|
||||
test "ash resolves inverse relationship automatically" do
|
||||
{:ok, member} = Membership.create_member(@valid_member_attrs)
|
||||
|
||||
user_attrs = Map.put(@valid_user_attrs, :member, %{id: member.id})
|
||||
{:ok, user} = Accounts.create_user(user_attrs)
|
||||
|
||||
# Load relationships
|
||||
{:ok, user_with_member} = Ash.get(Mv.Accounts.User, user.id, load: [:member])
|
||||
{:ok, member_with_user} = Ash.get(Mv.Membership.Member, member.id, load: [:user])
|
||||
|
||||
assert user_with_member.member.id == member.id
|
||||
assert member_with_user.user.id == user.id
|
||||
end
|
||||
|
||||
test "member can find associated user" do
|
||||
{:ok, member} = Membership.create_member(@valid_member_attrs)
|
||||
|
||||
{:ok, user} = Accounts.create_user(%{email: "test3@example.com", member: %{id: member.id}})
|
||||
{:ok, member_with_user} = Ash.get(Mv.Membership.Member, member.id, load: [:user])
|
||||
assert member_with_user.user.id == user.id
|
||||
end
|
||||
end
|
||||
|
||||
describe "User-Member Relationship - Preventing Duplicates" do
|
||||
@valid_user_attrs %{
|
||||
email: "test4@example.com"
|
||||
}
|
||||
|
||||
@valid_member_attrs %{
|
||||
first_name: "Charlie",
|
||||
last_name: "Brown",
|
||||
email: "charlie@example.com"
|
||||
}
|
||||
|
||||
test "prevents overwriting a member of already linked user on update" do
|
||||
{:ok, existing_member} = Membership.create_member(@valid_member_attrs)
|
||||
|
||||
user_attrs = Map.put(@valid_user_attrs, :member, %{id: existing_member.id})
|
||||
{:ok, user} = Accounts.create_user(user_attrs)
|
||||
|
||||
{:ok, member2} =
|
||||
Membership.create_member(%{
|
||||
first_name: "Dave",
|
||||
last_name: "Wilson",
|
||||
email: "dave@example.com"
|
||||
})
|
||||
|
||||
assert {:error, %Ash.Error.Invalid{}} =
|
||||
Accounts.update_user(user, %{member: %{id: member2.id}})
|
||||
end
|
||||
|
||||
test "prevents linking user to already linked member on update" do
|
||||
{:ok, existing_user} = Accounts.create_user(@valid_user_attrs)
|
||||
{:ok, member} = Membership.create_member(@valid_member_attrs)
|
||||
|
||||
{:ok, _updated_user} = Accounts.update_user(existing_user, %{member: %{id: member.id}})
|
||||
|
||||
{:ok, user2} = Accounts.create_user(%{email: "test5@example.com"})
|
||||
|
||||
assert {:error, %Ash.Error.Invalid{}} =
|
||||
Accounts.update_user(user2, %{member: %{id: member.id}})
|
||||
end
|
||||
|
||||
test "prevents linking member to already linked user on creation" do
|
||||
{:ok, existing_member} = Membership.create_member(@valid_member_attrs)
|
||||
|
||||
user_attrs = Map.put(@valid_user_attrs, :member, %{id: existing_member.id})
|
||||
{:ok, user} = Accounts.create_user(user_attrs)
|
||||
|
||||
assert {:error, %Ash.Error.Invalid{}} =
|
||||
Membership.create_member(%{
|
||||
first_name: "Dave",
|
||||
last_name: "Wilson",
|
||||
email: "dave@example.com",
|
||||
user: %{id: user.id}
|
||||
})
|
||||
end
|
||||
|
||||
test "prevents linking user to already linked member on creation" do
|
||||
{:ok, existing_user} = Accounts.create_user(@valid_user_attrs)
|
||||
{:ok, member} = Membership.create_member(@valid_member_attrs)
|
||||
|
||||
{:ok, _updated_user} = Accounts.update_user(existing_user, %{member: %{id: member.id}})
|
||||
|
||||
assert {:error, %Ash.Error.Invalid{}} =
|
||||
Accounts.create_user(%{
|
||||
email: "test5@example.com",
|
||||
member: %{id: member.id}
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -255,7 +255,7 @@ defmodule MvWeb.UserLive.FormTest do
|
|||
conn = Plug.Test.init_test_session(conn, locale: "de")
|
||||
{:ok, _view, html} = live(conn, "/users/new")
|
||||
|
||||
assert html =~ "Neuer Benutzer"
|
||||
assert html =~ "Neue*r Benutzer*in"
|
||||
assert html =~ "E-Mail"
|
||||
assert html =~ "Passwort setzen"
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ defmodule MvWeb.UserLive.IndexTest do
|
|||
conn = conn_with_oidc_user(conn)
|
||||
conn = Plug.Test.init_test_session(conn, locale: "de")
|
||||
{:ok, _view, html} = live(conn, "/users")
|
||||
assert html =~ "Benutzer auflisten"
|
||||
assert html =~ "Benutzer*innen auflisten"
|
||||
end
|
||||
|
||||
test "shows translated title in English", %{conn: conn} do
|
||||
|
|
@ -362,8 +362,8 @@ defmodule MvWeb.UserLive.IndexTest do
|
|||
conn = Plug.Test.init_test_session(conn, locale: "de")
|
||||
{:ok, _view, html} = live(conn, "/users")
|
||||
|
||||
assert html =~ "Alle Benutzer auswählen"
|
||||
assert html =~ "Benutzer auswählen"
|
||||
assert html =~ "Alle Benutzer*innen auswählen"
|
||||
assert html =~ "Benutzer*in auswählen"
|
||||
end
|
||||
|
||||
test "shows English translations for selection", %{conn: conn} do
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue
Should we add this comment here as well?