WIP feat: member user relation

This commit is contained in:
Moritz 2025-07-24 20:15:01 +02:00
parent 997691746a
commit 4e6f5a517a
Signed by: moritz
GPG key ID: 1020A035E5DD0824
35 changed files with 1208 additions and 192 deletions

View file

@ -21,4 +21,15 @@ defmodule Mv.Accounts do
resource Mv.Accounts.Token resource Mv.Accounts.Token
end end
@doc """
Register a new user with password using AshAuthentication's standard action.
This creates a user and the notifier will automatically create a member.
"""
def register_with_password(params) do
# Use AshAuthentication's standard register_with_password action
Mv.Accounts.User
|> Ash.Changeset.for_create(:register_with_password, params)
|> Ash.create(domain: __MODULE__)
end
end end

View file

@ -5,7 +5,8 @@ defmodule Mv.Accounts.User do
use Ash.Resource, use Ash.Resource,
domain: Mv.Accounts, domain: Mv.Accounts,
data_layer: AshPostgres.DataLayer, data_layer: AshPostgres.DataLayer,
extensions: [AshAuthentication] extensions: [AshAuthentication],
notifiers: [Mv.Accounts.User.MemberCreationNotifier]
# authorizers: [Ash.Policy.Authorizer] # authorizers: [Ash.Policy.Authorizer]
@ -64,11 +65,11 @@ defmodule Mv.Accounts.User do
defaults [:read, :create, :destroy, :update] defaults [:read, :create, :destroy, :update]
create :create_user do create :create_user do
accept [:email] accept [:email, :member_id]
end end
update :update_user do update :update_user do
accept [:email] accept [:email, :member_id]
end end
# Admin action for direct password changes in admin panel # Admin action for direct password changes in admin panel
@ -121,9 +122,16 @@ defmodule Mv.Accounts.User do
# Global validations - applied to all relevant actions # Global validations - applied to all relevant actions
validations do validations do
# Password strength policy: minimum 8 characters for all password-related actions # Password strength policy: minimum 8 characters
# Note: register_with_password has built-in AshAuthentication validation, but admin_set_password doesn't
validate string_length(:password, min: 8) do validate string_length(:password, min: 8) do
where action_is([:register_with_password, :admin_set_password]) # Only needed for admin actions, AshAuthentication handles register_with_password
where action_is([:admin_set_password])
end
# Email uniqueness for registration actions
validate attribute_does_not_equal(:email, nil) do
where action_is([:register_with_password, :register_with_rauthy])
end end
end end
@ -143,6 +151,7 @@ defmodule Mv.Accounts.User do
attribute :email, :ci_string, allow_nil?: false, public?: true attribute :email, :ci_string, allow_nil?: false, public?: true
attribute :hashed_password, :string, sensitive?: true, allow_nil?: true attribute :hashed_password, :string, sensitive?: true, allow_nil?: true
attribute :oidc_id, :string, allow_nil?: true attribute :oidc_id, :string, allow_nil?: true
attribute :admin?, :boolean, allow_nil?: false, default: false, public?: true
end end
relationships do relationships do
@ -152,6 +161,7 @@ defmodule Mv.Accounts.User do
identities do identities do
identity :unique_email, [:email] identity :unique_email, [:email]
identity :unique_oidc_id, [:oidc_id] identity :unique_oidc_id, [:oidc_id]
identity :unique_member_id, [:member_id]
end end
# You can customize this if you wish, but this is a safe default that # You can customize this if you wish, but this is a safe default that

View file

@ -0,0 +1,71 @@
defmodule Mv.Accounts.User.MemberCreationNotifier do
@moduledoc """
Notifier that automatically creates a member for newly registered users.
This runs after user creation/registration and ensures every user has an associated member.
It's designed to work with AshAuthentication without interfering with LiveView integration.
"""
use Ash.Notifier
require Logger
@impl Ash.Notifier
def notify(%Ash.Notifier.Notification{
action: %{name: action_name},
resource: Mv.Accounts.User,
data: user
})
when action_name in [:register_with_password, :register_with_rauthy, :create_user] do
# Only create member if user doesn't already have one
if should_create_member?(user) do
create_member_for_user(user)
end
:ok
end
@impl Ash.Notifier
def notify(_), do: :ok
defp should_create_member?(user) do
# Check if user has a member_id and if that member actually exists
case user.member_id do
nil ->
true
member_id ->
case Ash.get(Mv.Membership.Member, member_id, domain: Mv.Membership) do
{:ok, _member} -> false
{:error, _} -> true
end
end
end
defp create_member_for_user(user) do
member_params = %{
email: to_string(user.email),
first_name: "User",
last_name: "Generated"
}
case Mv.Membership.create_member(member_params) do
{:ok, member} ->
# Update user with member_id
case Ash.Changeset.for_update(user, :update_user, %{member_id: member.id})
|> Ash.update(domain: Mv.Accounts) do
{:ok, _updated_user} ->
Logger.info(
"Successfully created and assigned member #{member.id} to user #{user.id}"
)
{:error, error} ->
Logger.warning(
"Failed to assign member #{member.id} to user #{user.id}: #{inspect(error)}"
)
end
{:error, error} ->
Logger.warning("Failed to create member for user #{user.id}: #{inspect(error)}")
end
end
end

View file

@ -170,5 +170,6 @@ defmodule Mv.Membership.Member do
relationships do relationships do
has_many :properties, Mv.Membership.Property has_many :properties, Mv.Membership.Property
has_one :user, Mv.Accounts.User, destination_attribute: :member_id
end end
end end

View file

@ -10,6 +10,7 @@ defmodule Mv.Membership do
resource Mv.Membership.Member do resource Mv.Membership.Member do
define :create_member, action: :create_member define :create_member, action: :create_member
define :list_members, action: :read define :list_members, action: :read
define :get_member!, action: :read, get_by: [:id]
define :update_member, action: :update_member define :update_member, action: :update_member
define :destroy_member, action: :destroy define :destroy_member, action: :destroy
end end

View file

@ -24,6 +24,7 @@ defmodule MvWeb.Layouts do
""" """
attr :flash, :map, required: true, doc: "the map of flash messages" attr :flash, :map, required: true, doc: "the map of flash messages"
attr :current_user, :map, default: nil, doc: "the current user"
attr :current_scope, :map, attr :current_scope, :map,
default: nil, default: nil,
@ -33,7 +34,7 @@ defmodule MvWeb.Layouts do
def app(assigns) do def app(assigns) do
~H""" ~H"""
<.navbar /> <.navbar current_user={@current_user} />
<main class="px-4 py-20 sm:px-6 lg:px-16"> <main class="px-4 py-20 sm:px-6 lg:px-16">
<div class="mx-auto max-full space-y-4"> <div class="mx-auto max-full space-y-4">
{render_slot(@inner_block)} {render_slot(@inner_block)}

View file

@ -5,6 +5,8 @@ defmodule MvWeb.Layouts.Navbar do
use Phoenix.Component use Phoenix.Component
use Gettext, backend: MvWeb.Gettext use Gettext, backend: MvWeb.Gettext
attr :current_user, :map, default: nil
def navbar(assigns) do def navbar(assigns) do
~H""" ~H"""
<header class="navbar bg-base-100 shadow-sm"> <header class="navbar bg-base-100 shadow-sm">
@ -65,7 +67,7 @@ defmodule MvWeb.Layouts.Navbar do
class="menu menu-sm dropdown-content bg-base-100 rounded-box z-1 mt-3 w-52 p-2 shadow" class="menu menu-sm dropdown-content bg-base-100 rounded-box z-1 mt-3 w-52 p-2 shadow"
> >
<li> <li>
<a> <a :if={@current_user} href={"/users/#{@current_user.id}"}>
{gettext("Profil")} {gettext("Profil")}
</a> </a>
</li> </li>

View file

@ -4,7 +4,7 @@ defmodule MvWeb.MemberLive.Form do
@impl true @impl true
def render(assigns) do def render(assigns) do
~H""" ~H"""
<Layouts.app flash={@flash}> <Layouts.app flash={@flash} current_user={@current_user}>
<.header> <.header>
{@page_title} {@page_title}
<:subtitle> <:subtitle>
@ -155,7 +155,7 @@ defmodule MvWeb.MemberLive.Form do
AshPhoenix.Form.for_update( AshPhoenix.Form.for_update(
member, member,
:update_member, :update_member,
api: Mv.Membership, domain: Mv.Membership,
as: "member", as: "member",
params: params, params: params,
forms: [auto?: true] forms: [auto?: true]
@ -172,7 +172,7 @@ defmodule MvWeb.MemberLive.Form do
AshPhoenix.Form.for_create( AshPhoenix.Form.for_create(
Mv.Membership.Member, Mv.Membership.Member,
:create_member, :create_member,
api: Mv.Membership, domain: Mv.Membership,
as: "member", as: "member",
params: %{"properties" => socket.assigns[:initial_properties]}, params: %{"properties" => socket.assigns[:initial_properties]},
forms: [auto?: true] forms: [auto?: true]

View file

@ -1,4 +1,4 @@
<Layouts.app flash={@flash}> <Layouts.app flash={@flash} current_user={@current_user}>
<.header> <.header>
{gettext("Members")} {gettext("Members")}
<:actions> <:actions>

View file

@ -5,7 +5,7 @@ defmodule MvWeb.MemberLive.Show do
@impl true @impl true
def render(assigns) do def render(assigns) do
~H""" ~H"""
<Layouts.app flash={@flash}> <Layouts.app flash={@flash} current_user={@current_user}>
<.header> <.header>
{@member.first_name} {@member.last_name} {@member.first_name} {@member.last_name}
<:subtitle>{gettext("This is a member record from your database.")}</:subtitle> <:subtitle>{gettext("This is a member record from your database.")}</:subtitle>

View file

@ -4,7 +4,7 @@ defmodule MvWeb.PropertyLive.Form do
@impl true @impl true
def render(assigns) do def render(assigns) do
~H""" ~H"""
<Layouts.app flash={@flash}> <Layouts.app flash={@flash} current_user={@current_user}>
<.header> <.header>
{@page_title} {@page_title}
<:subtitle>{gettext("Use this form to manage property records in your database.")}</:subtitle> <:subtitle>{gettext("Use this form to manage property records in your database.")}</:subtitle>

View file

@ -4,7 +4,7 @@ defmodule MvWeb.PropertyLive.Index do
@impl true @impl true
def render(assigns) do def render(assigns) do
~H""" ~H"""
<Layouts.app flash={@flash}> <Layouts.app flash={@flash} current_user={@current_user}>
<.header> <.header>
Listing Properties Listing Properties
<:actions> <:actions>

View file

@ -4,7 +4,7 @@ defmodule MvWeb.PropertyLive.Show do
@impl true @impl true
def render(assigns) do def render(assigns) do
~H""" ~H"""
<Layouts.app flash={@flash}> <Layouts.app flash={@flash} current_user={@current_user}>
<.header> <.header>
Property {@property.id} Property {@property.id}
<:subtitle>This is a property record from your database.</:subtitle> <:subtitle>This is a property record from your database.</:subtitle>

View file

@ -4,7 +4,7 @@ defmodule MvWeb.PropertyTypeLive.Form do
@impl true @impl true
def render(assigns) do def render(assigns) do
~H""" ~H"""
<Layouts.app flash={@flash}> <Layouts.app flash={@flash} current_user={@current_user}>
<.header> <.header>
{@page_title} {@page_title}
<:subtitle> <:subtitle>

View file

@ -4,7 +4,7 @@ defmodule MvWeb.PropertyTypeLive.Index do
@impl true @impl true
def render(assigns) do def render(assigns) do
~H""" ~H"""
<Layouts.app flash={@flash}> <Layouts.app flash={@flash} current_user={@current_user}>
<.header> <.header>
Listing Property types Listing Property types
<:actions> <:actions>

View file

@ -4,7 +4,7 @@ defmodule MvWeb.PropertyTypeLive.Show do
@impl true @impl true
def render(assigns) do def render(assigns) do
~H""" ~H"""
<Layouts.app flash={@flash}> <Layouts.app flash={@flash} current_user={@current_user}>
<.header> <.header>
Property type {@property_type.id} Property type {@property_type.id}
<:subtitle>This is a property_type record from your database.</:subtitle> <:subtitle>This is a property_type record from your database.</:subtitle>

View file

@ -4,7 +4,7 @@ defmodule MvWeb.UserLive.Form do
@impl true @impl true
def render(assigns) do def render(assigns) do
~H""" ~H"""
<Layouts.app flash={@flash}> <Layouts.app flash={@flash} current_user={@current_user}>
<.header> <.header>
{@page_title} {@page_title}
<:subtitle>{gettext("Use this form to manage user records in your database.")}</:subtitle> <:subtitle>{gettext("Use this form to manage user records in your database.")}</:subtitle>
@ -13,6 +13,53 @@ defmodule MvWeb.UserLive.Form do
<.form for={@form} id="user-form" phx-change="validate" phx-submit="save"> <.form for={@form} id="user-form" phx-change="validate" phx-submit="save">
<.input field={@form[:email]} label={gettext("Email")} required type="email" /> <.input field={@form[:email]} label={gettext("Email")} required type="email" />
<!-- Member Assignment Section -->
<div class="mt-6 space-y-4">
<h3 class="text-lg font-medium">{gettext("Member Assignment")}</h3>
<label class="flex items-center space-x-2">
<input
type="radio"
name="member_assignment_mode"
value="create_new"
phx-click="set_member_mode"
phx-value-mode="create_new"
checked={@member_assignment_mode == "create_new"}
class="radio radio-sm"
/>
<span class="text-sm">
{gettext("Create new member automatically")}
</span>
</label>
<label class="flex items-center space-x-2">
<input
type="radio"
name="member_assignment_mode"
value="assign_existing"
phx-click="set_member_mode"
phx-value-mode="assign_existing"
checked={@member_assignment_mode == "assign_existing"}
class="radio radio-sm"
/>
<span class="text-sm">
{gettext("Assign to existing member")}
</span>
</label>
<%= if @member_assignment_mode == "assign_existing" do %>
<div class="ml-6 mt-2">
<.input
field={@form[:member_id]}
label={gettext("Select Member")}
type="select"
options={@available_members}
prompt={gettext("Choose a member...")}
/>
</div>
<% end %>
</div>
<!-- Password Section --> <!-- Password Section -->
<div class="mt-6"> <div class="mt-6">
<label class="flex items-center space-x-2"> <label class="flex items-center space-x-2">
@ -109,12 +156,25 @@ defmodule MvWeb.UserLive.Form do
action = if is_nil(user), do: gettext("New"), else: gettext("Edit") action = if is_nil(user), do: gettext("New"), else: gettext("Edit")
page_title = action <> " " <> gettext("User") page_title = action <> " " <> gettext("User")
# Load available members that have no user assigned
{:ok, available_members} = Mv.Membership.list_members()
available_members_with_user = Ash.load!(available_members, :user)
available_member_options =
available_members_with_user
|> Enum.filter(fn member -> is_nil(member.user) end)
|> Enum.map(fn member ->
{"#{member.first_name} #{member.last_name} (#{member.email})", member.id}
end)
{:ok, {:ok,
socket socket
|> assign(:return_to, return_to(params["return_to"])) |> assign(:return_to, return_to(params["return_to"]))
|> assign(user: user) |> assign(user: user)
|> assign(:page_title, page_title) |> assign(:page_title, page_title)
|> assign(:show_password_fields, false) |> assign(:show_password_fields, false)
|> assign(:member_assignment_mode, "create_new")
|> assign(:available_members, available_member_options)
|> assign_form()} |> assign_form()}
end end
@ -133,6 +193,15 @@ defmodule MvWeb.UserLive.Form do
{:noreply, socket} {:noreply, socket}
end end
def handle_event("set_member_mode", %{"mode" => mode}, socket) do
socket =
socket
|> assign(:member_assignment_mode, mode)
|> assign_form()
{:noreply, socket}
end
def handle_event("validate", %{"user" => user_params}, socket) do def handle_event("validate", %{"user" => user_params}, socket) do
{:noreply, assign(socket, form: AshPhoenix.Form.validate(socket.assigns.form, user_params))} {:noreply, assign(socket, form: AshPhoenix.Form.validate(socket.assigns.form, user_params))}
end end
@ -161,14 +230,30 @@ defmodule MvWeb.UserLive.Form do
if user do if user do
# For existing users, use admin password action if password fields are shown # For existing users, use admin password action if password fields are shown
action = if show_password_fields, do: :admin_set_password, else: :update_user action = if show_password_fields, do: :admin_set_password, else: :update_user
AshPhoenix.Form.for_update(user, action, domain: Mv.Accounts, as: "user")
AshPhoenix.Form.for_update(user, action,
as: "user",
actor: socket.assigns.current_user,
domain: Mv.Accounts
)
else else
# For new users, use password registration if password fields are shown # For new users, use password registration if password fields are shown
action = if show_password_fields, do: :register_with_password, else: :create_user action = if show_password_fields, do: :register_with_password, else: :create_user
# Only include member_id if assign_existing mode is selected AND not using password action
accept =
if socket.assigns.member_assignment_mode == "assign_existing" and
not show_password_fields do
[:email, :member_id]
else
[:email]
end
AshPhoenix.Form.for_create(Mv.Accounts.User, action, AshPhoenix.Form.for_create(Mv.Accounts.User, action,
as: "user",
actor: socket.assigns.current_user,
domain: Mv.Accounts, domain: Mv.Accounts,
as: "user" accept: accept
) )
end end

View file

@ -4,7 +4,10 @@ defmodule MvWeb.UserLive.Index do
@impl true @impl true
def mount(_params, _session, socket) do def mount(_params, _session, socket) do
users = Ash.read!(Mv.Accounts.User, domain: Mv.Accounts) users =
Ash.read!(Mv.Accounts.User, domain: Mv.Accounts)
|> Ash.load!(:member)
sorted = Enum.sort_by(users, & &1.email) sorted = Enum.sort_by(users, & &1.email)
{:ok, {:ok,

View file

@ -1,4 +1,4 @@
<Layouts.app flash={@flash}> <Layouts.app flash={@flash} current_user={@current_user}>
<.header> <.header>
{gettext("Listing Users")} {gettext("Listing Users")}
<:actions> <:actions>
@ -49,6 +49,11 @@
> >
{user.email} {user.email}
</:col> </:col>
<:col :let={user} label={gettext("Member")}>
{if user.member,
do: "#{user.member.first_name} #{user.member.last_name}",
else: gettext("No member")}
</:col>
<:col :let={user} label={gettext("OIDC ID")}>{user.oidc_id}</:col> <:col :let={user} label={gettext("OIDC ID")}>{user.oidc_id}</:col>
<:action :let={user}> <:action :let={user}>

View file

@ -4,7 +4,7 @@ defmodule MvWeb.UserLive.Show do
@impl true @impl true
def render(assigns) do def render(assigns) do
~H""" ~H"""
<Layouts.app flash={@flash}> <Layouts.app flash={@flash} current_user={@current_user}>
<.header> <.header>
{gettext("User")} {@user.email} {gettext("User")} {@user.email}
<:subtitle>{gettext("This is a user record from your database.")}</:subtitle> <:subtitle>{gettext("This is a user record from your database.")}</:subtitle>
@ -22,6 +22,14 @@ defmodule MvWeb.UserLive.Show do
<.list> <.list>
<:item title={gettext("ID")}>{@user.id}</:item> <:item title={gettext("ID")}>{@user.id}</:item>
<:item title={gettext("Email")}>{@user.email}</:item> <:item title={gettext("Email")}>{@user.email}</:item>
<:item title={gettext("Member")}>
<div :if={@user.member}>
<.link navigate={~p"/members/#{@user.member.id}"} class="link link-primary">
{@user.member.first_name} {@user.member.last_name} ({@user.member.email})
</.link>
</div>
<span :if={!@user.member}>{gettext("No member assigned")}</span>
</:item>
<:item title={gettext("OIDC ID")}>{@user.oidc_id || gettext("Not set")}</:item> <:item title={gettext("OIDC ID")}>{@user.oidc_id || gettext("Not set")}</:item>
<:item title={gettext("Password Authentication")}> <:item title={gettext("Password Authentication")}>
{if @user.hashed_password, do: gettext("Enabled"), else: gettext("Not enabled")} {if @user.hashed_password, do: gettext("Enabled"), else: gettext("Not enabled")}
@ -33,9 +41,13 @@ defmodule MvWeb.UserLive.Show do
@impl true @impl true
def mount(%{"id" => id}, _session, socket) do def mount(%{"id" => id}, _session, socket) do
user =
Ash.get!(Mv.Accounts.User, id, domain: Mv.Accounts)
|> Ash.load!(:member)
{:ok, {:ok,
socket socket
|> assign(:page_title, gettext("Show User")) |> assign(:page_title, gettext("Show User"))
|> assign(:user, Ash.get!(Mv.Accounts.User, id, domain: Mv.Accounts))} |> assign(:user, user)}
end end
end end

View file

@ -58,6 +58,7 @@
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"}, "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"},
"phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"}, "phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"},
"phoenix_view": {:hex, :phoenix_view, "2.0.4", "b45c9d9cf15b3a1af5fb555c674b525391b6a1fe975f040fb4d913397b31abf4", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "4e992022ce14f31fe57335db27a28154afcc94e9983266835bb3040243eb620b"}, "phoenix_view": {:hex, :phoenix_view, "2.0.4", "b45c9d9cf15b3a1af5fb555c674b525391b6a1fe975f040fb4d913397b31abf4", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "4e992022ce14f31fe57335db27a28154afcc94e9983266835bb3040243eb620b"},
"picosat_elixir": {:hex, :picosat_elixir, "0.2.3", "bf326d0f179fbb3b706bb2c15fbc367dacfa2517157d090fdfc32edae004c597", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "f76c9db2dec9d2561ffaa9be35f65403d53e984e8cd99c832383b7ab78c16c66"},
"plug": {:hex, :plug, "1.18.1", "5067f26f7745b7e31bc3368bc1a2b818b9779faa959b49c934c17730efc911cf", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "57a57db70df2b422b564437d2d33cf8d33cd16339c1edb190cd11b1a3a546cc2"}, "plug": {:hex, :plug, "1.18.1", "5067f26f7745b7e31bc3368bc1a2b818b9779faa959b49c934c17730efc911cf", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "57a57db70df2b422b564437d2d33cf8d33cd16339c1edb190cd11b1a3a546cc2"},
"plug_crypto": {:hex, :plug_crypto, "2.1.1", "19bda8184399cb24afa10be734f84a16ea0a2bc65054e23a62bb10f06bc89491", [:mix], [], "hexpm", "6470bce6ffe41c8bd497612ffde1a7e4af67f36a15eea5f921af71cf3e11247c"}, "plug_crypto": {:hex, :plug_crypto, "2.1.1", "19bda8184399cb24afa10be734f84a16ea0a2bc65054e23a62bb10f06bc89491", [:mix], [], "hexpm", "6470bce6ffe41c8bd497612ffde1a7e4af67f36a15eea5f921af71cf3e11247c"},
"postgrex": {:hex, :postgrex, "0.21.1", "2c5cc830ec11e7a0067dd4d623c049b3ef807e9507a424985b8dcf921224cd88", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "27d8d21c103c3cc68851b533ff99eef353e6a0ff98dc444ea751de43eb48bdac"}, "postgrex": {:hex, :postgrex, "0.21.1", "2c5cc830ec11e7a0067dd4d623c049b3ef807e9507a424985b8dcf921224cd88", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "27d8d21c103c3cc68851b533ff99eef353e6a0ff98dc444ea751de43eb48bdac"},

View file

@ -16,13 +16,13 @@ msgid "Actions"
msgstr "Aktionen" msgstr "Aktionen"
#: lib/mv_web/live/member_live/index.html.heex:77 #: lib/mv_web/live/member_live/index.html.heex:77
#: lib/mv_web/live/user_live/index.html.heex:69 #: lib/mv_web/live/user_live/index.html.heex:70
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Are you sure?" msgid "Are you sure?"
msgstr "Bist du sicher?" msgstr "Bist du sicher?"
#: lib/mv_web/components/layouts.ex:71 #: lib/mv_web/components/layouts.ex:72
#: lib/mv_web/components/layouts.ex:83 #: lib/mv_web/components/layouts.ex:84
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Attempting to reconnect" msgid "Attempting to reconnect"
msgstr "Verbindung wird wiederhergestellt" msgstr "Verbindung wird wiederhergestellt"
@ -35,14 +35,14 @@ msgid "City"
msgstr "Stadt" msgstr "Stadt"
#: lib/mv_web/live/member_live/index.html.heex:79 #: lib/mv_web/live/member_live/index.html.heex:79
#: lib/mv_web/live/user_live/index.html.heex:71 #: lib/mv_web/live/user_live/index.html.heex:72
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Delete" msgid "Delete"
msgstr "Löschen" msgstr "Löschen"
#: lib/mv_web/live/member_live/index.html.heex:71 #: lib/mv_web/live/member_live/index.html.heex:71
#: lib/mv_web/live/user_live/form.ex:109 #: lib/mv_web/live/user_live/form.ex:156
#: lib/mv_web/live/user_live/index.html.heex:63 #: lib/mv_web/live/user_live/index.html.heex:64
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Edit" msgid "Edit"
msgstr "Bearbeite" msgstr "Bearbeite"
@ -57,7 +57,7 @@ msgstr "Mitglied bearbeiten"
#: lib/mv_web/live/member_live/index.html.heex:58 #: 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/show.ex:27
#: lib/mv_web/live/user_live/form.ex:14 #: lib/mv_web/live/user_live/form.ex:14
#: lib/mv_web/live/user_live/index.html.heex:48 #: 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:24
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Email" msgid "Email"
@ -88,17 +88,17 @@ msgid "New Member"
msgstr "Neues Mitglied" msgstr "Neues Mitglied"
#: lib/mv_web/live/member_live/index.html.heex:68 #: lib/mv_web/live/member_live/index.html.heex:68
#: lib/mv_web/live/user_live/index.html.heex:60 #: lib/mv_web/live/user_live/index.html.heex:61
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Show" msgid "Show"
msgstr "Anzeigen" msgstr "Anzeigen"
#: lib/mv_web/components/layouts.ex:78 #: lib/mv_web/components/layouts.ex:79
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Something went wrong!" msgid "Something went wrong!"
msgstr "Etwas ist schiefgelaufen!" msgstr "Etwas ist schiefgelaufen!"
#: lib/mv_web/components/layouts.ex:66 #: lib/mv_web/components/layouts.ex:67
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "We can't find the internet" msgid "We can't find the internet"
msgstr "Keine Internetverbindung gefunden" msgstr "Keine Internetverbindung gefunden"
@ -167,7 +167,7 @@ msgstr "Mitglied speichern"
#: lib/mv_web/live/member_live/form.ex:49 #: lib/mv_web/live/member_live/form.ex:49
#: lib/mv_web/live/property_live/form.ex:41 #: lib/mv_web/live/property_live/form.ex:41
#: lib/mv_web/live/property_type_live/form.ex:29 #: lib/mv_web/live/property_type_live/form.ex:29
#: lib/mv_web/live/user_live/form.ex:92 #: lib/mv_web/live/user_live/form.ex:139
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Saving..." msgid "Saving..."
msgstr "Speichern..." msgstr "Speichern..."
@ -261,7 +261,7 @@ msgstr "Ihr Passwort wurde erfolgreich zurückgesetzt"
#: lib/mv_web/live/member_live/form.ex:52 #: lib/mv_web/live/member_live/form.ex:52
#: lib/mv_web/live/property_live/form.ex:44 #: lib/mv_web/live/property_live/form.ex:44
#: lib/mv_web/live/property_type_live/form.ex:32 #: lib/mv_web/live/property_type_live/form.ex:32
#: lib/mv_web/live/user_live/form.ex:95 #: lib/mv_web/live/user_live/form.ex:142
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Cancel" msgid "Cancel"
msgstr "Abbrechen" msgstr "Abbrechen"
@ -286,7 +286,7 @@ msgstr "Beschreibung"
msgid "Edit User" msgid "Edit User"
msgstr "Benutzer bearbeiten" msgstr "Benutzer bearbeiten"
#: lib/mv_web/live/user_live/show.ex:27 #: lib/mv_web/live/user_live/show.ex:35
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Enabled" msgid "Enabled"
msgstr "Aktiviert" msgstr "Aktiviert"
@ -301,29 +301,79 @@ msgstr "ID"
msgid "Immutable" msgid "Immutable"
msgstr "Unveränderlich" msgstr "Unveränderlich"
#: lib/mv_web/components/layouts/navbar.ex:73 #: lib/mv_web/components/layouts/navbar.ex:75
#, elixir-autogen, elixir-format #, elixir-autogen
msgid "Logout" msgid "Logout"
msgstr "Abmelden" msgstr "Abmelden"
#: lib/mv_web/live/user_live/index.ex:12 #: lib/mv_web/components/layouts/navbar.ex:16
#: lib/mv_web/live/user_live/index.html.heex:3
#, elixir-autogen, elixir-format
msgid "Listing Users"
msgstr "Benutzer 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/live/member_live/index.ex:12
#: lib/mv_web/live/member_live/index.html.heex:3 #: lib/mv_web/live/member_live/index.html.heex:3
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Members" msgid "Members"
msgstr "Mitglieder" msgstr "Mitglieder"
#: lib/mv_web/live/property_live/form.ex:27
#: lib/mv_web/live/user_live/index.html.heex:52
#: lib/mv_web/live/user_live/show.ex:25
#, elixir-autogen, elixir-format
msgid "Member"
msgstr "Mitglied"
#: lib/mv_web/live/user_live/index.html.heex:55
#, elixir-autogen
msgid "No member"
msgstr "Kein Mitglied"
#: lib/mv_web/live/user_live/show.ex:31
#, elixir-autogen
msgid "No member assigned"
msgstr "Kein Mitglied zugeordnet"
#, fuzzy
msgid "Profile"
msgstr "Profil"
msgid "Edit your member information"
msgstr "Bearbeite deine Mitgliedsdaten"
#, fuzzy
msgid "Save Profile"
msgstr "Profil"
msgid "Profile updated successfully"
msgstr "Profil erfolgreich aktualisiert"
msgid "No member profile found"
msgstr "Kein Mitgliederprofil gefunden"
#: lib/mv_web/live/user_live/form.ex:18
#, elixir-autogen, fuzzy
msgid "Member Assignment"
msgstr "Mitglieder"
msgid "Create new member automatically"
msgstr "Neues Mitglied automatisch erstellen"
msgid "Assign to existing member"
msgstr "Bestehendem Mitglied zuordnen"
#: lib/mv_web/live/user_live/form.ex:54
#, elixir-autogen, fuzzy
msgid "Select Member"
msgstr "Mitglied auswählen"
#: lib/mv_web/live/user_live/form.ex:57
#, elixir-autogen, fuzzy
msgid "Choose a member..."
msgstr "Mitglied auswählen"
#: lib/mv_web/live/user_live/index.ex:15
#: lib/mv_web/live/user_live/index.html.heex:3
#, elixir-autogen, elixir-format
msgid "Listing Users"
msgstr "Benutzer auflisten"
#: lib/mv_web/live/member_live/index.html.heex:50 #: lib/mv_web/live/member_live/index.html.heex:50
#: lib/mv_web/live/property_type_live/form.ex:16 #: lib/mv_web/live/property_type_live/form.ex:16
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
@ -335,29 +385,29 @@ msgstr "Name"
msgid "New User" msgid "New User"
msgstr "Neuer Benutzer" msgstr "Neuer Benutzer"
#: lib/mv_web/live/user_live/show.ex:27 #: lib/mv_web/live/user_live/show.ex:35
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Not enabled" msgid "Not enabled"
msgstr "Nicht aktiviert" msgstr "Nicht aktiviert"
#: lib/mv_web/live/user_live/show.ex:25 #: lib/mv_web/live/user_live/show.ex:33
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Not set" msgid "Not set"
msgstr "Nicht gesetzt" msgstr "Nicht gesetzt"
#: lib/mv_web/live/user_live/form.ex:75 #: lib/mv_web/live/user_live/form.ex:122
#: lib/mv_web/live/user_live/form.ex:83 #: lib/mv_web/live/user_live/form.ex:130
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Note" msgid "Note"
msgstr "Hinweis" msgstr "Hinweis"
#: lib/mv_web/live/user_live/index.html.heex:56 #: lib/mv_web/live/user_live/index.html.heex:57
#: lib/mv_web/live/user_live/show.ex:25 #: lib/mv_web/live/user_live/show.ex:33
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "OIDC ID" msgid "OIDC ID"
msgstr "OIDC ID" msgstr "OIDC ID"
#: lib/mv_web/live/user_live/show.ex:26 #: lib/mv_web/live/user_live/show.ex:34
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Password Authentication" msgid "Password Authentication"
msgstr "Passwort-Authentifizierung" msgstr "Passwort-Authentifizierung"
@ -367,7 +417,7 @@ msgstr "Passwort-Authentifizierung"
msgid "Please select a property type first" msgid "Please select a property type first"
msgstr "Bitte wählen Sie zuerst einen Eigenschaftstyp" msgstr "Bitte wählen Sie zuerst einen Eigenschaftstyp"
#: lib/mv_web/components/layouts/navbar.ex:69 #: lib/mv_web/components/layouts/navbar.ex:71
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Profil" msgid "Profil"
msgstr "Profil" msgstr "Profil"
@ -412,17 +462,17 @@ msgstr "Alle Mitglieder auswählen"
msgid "Select member" msgid "Select member"
msgstr "Mitglied auswählen" msgstr "Mitglied auswählen"
#: lib/mv_web/components/layouts/navbar.ex:72 #: lib/mv_web/components/layouts/navbar.ex:74
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Settings" msgid "Settings"
msgstr "Einstellungen" msgstr "Einstellungen"
#: lib/mv_web/live/user_live/form.ex:93 #: lib/mv_web/live/user_live/form.ex:140
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Save User" msgid "Save User"
msgstr "Benutzer speichern" msgstr "Benutzer speichern"
#: lib/mv_web/live/user_live/show.ex:38 #: lib/mv_web/live/user_live/show.ex:50
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Show User" msgid "Show User"
msgstr "Benutzer anzeigen" msgstr "Benutzer anzeigen"
@ -452,7 +502,7 @@ msgstr "Dieses Formular dient zur Verwaltung von Mitgliedern und deren Eigenscha
msgid "Use this form to manage user records in your database." 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-Datensätze zu verwalten."
#: lib/mv_web/live/user_live/form.ex:110 #: lib/mv_web/live/user_live/form.ex:157
#: lib/mv_web/live/user_live/show.ex:9 #: lib/mv_web/live/user_live/show.ex:9
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "User" msgid "User"
@ -478,77 +528,77 @@ msgstr "aufsteigend"
msgid "descending" msgid "descending"
msgstr "absteigend" msgstr "absteigend"
#: lib/mv_web/live/user_live/form.ex:109 #: lib/mv_web/live/user_live/form.ex:156
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "New" msgid "New"
msgstr "Neuer" msgstr "Neuer"
#: lib/mv_web/live/user_live/form.ex:64 #: lib/mv_web/live/user_live/form.ex:111
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Admin Note" msgid "Admin Note"
msgstr "Administrator-Hinweis" msgstr "Administrator-Hinweis"
#: lib/mv_web/live/user_live/form.ex:64 #: lib/mv_web/live/user_live/form.ex:111
#, elixir-autogen, elixir-format #, 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." 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 können Sie direkt ein neues Passwort für diesen Benutzer setzen, wobei das gleiche sichere Ash Authentication System verwendet wird."
#: lib/mv_web/live/user_live/form.ex:55 #: lib/mv_web/live/user_live/form.ex:102
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "At least 8 characters" msgid "At least 8 characters"
msgstr "Mindestens 8 Zeichen" msgstr "Mindestens 8 Zeichen"
#: lib/mv_web/live/user_live/form.ex:27 #: lib/mv_web/live/user_live/form.ex:74
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Change Password" msgid "Change Password"
msgstr "Passwort ändern" msgstr "Passwort ändern"
#: lib/mv_web/live/user_live/form.ex:75 #: lib/mv_web/live/user_live/form.ex:122
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Check 'Change Password' above to set a new password for this user." 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 diesen Benutzer zu setzen."
#: lib/mv_web/live/user_live/form.ex:45 #: lib/mv_web/live/user_live/form.ex:92
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Confirm Password" msgid "Confirm Password"
msgstr "Passwort bestätigen" msgstr "Passwort bestätigen"
#: lib/mv_web/live/user_live/form.ex:57 #: lib/mv_web/live/user_live/form.ex:104
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Consider using special characters" msgid "Consider using special characters"
msgstr "Sonderzeichen empfohlen" msgstr "Sonderzeichen empfohlen"
#: lib/mv_web/live/user_live/form.ex:56 #: lib/mv_web/live/user_live/form.ex:103
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Include both letters and numbers" msgid "Include both letters and numbers"
msgstr "Buchstaben und Zahlen verwenden" msgstr "Buchstaben und Zahlen verwenden"
#: lib/mv_web/live/user_live/form.ex:35 #: lib/mv_web/live/user_live/form.ex:82
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Password" msgid "Password"
msgstr "Passwort" msgstr "Passwort"
#: lib/mv_web/live/user_live/form.ex:53 #: lib/mv_web/live/user_live/form.ex:100
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Password requirements" msgid "Password requirements"
msgstr "Passwort-Anforderungen" msgstr "Passwort-Anforderungen"
#: lib/mv_web/live/user_live/index.html.heex:25 #: lib/mv_web/live/user_live/index.html.heex:21
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Select all users" msgid "Select all users"
msgstr "Alle Benutzer auswählen" msgstr "Alle Benutzer auswählen"
#: lib/mv_web/live/user_live/index.html.heex:39 #: lib/mv_web/live/user_live/index.html.heex:35
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Select user" msgid "Select user"
msgstr "Benutzer auswählen" msgstr "Benutzer auswählen"
#: lib/mv_web/live/user_live/form.ex:27 #: lib/mv_web/live/user_live/form.ex:74
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Set Password" msgid "Set Password"
msgstr "Passwort setzen" msgstr "Passwort setzen"
#: lib/mv_web/live/user_live/form.ex:83 #: lib/mv_web/live/user_live/form.ex:130
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "User will be created without a password. Check 'Set Password' to add one." 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 wird ohne Passwort erstellt. Aktivieren Sie 'Passwort setzen', um eines hinzuzufügen."

View file

@ -17,13 +17,13 @@ msgid "Actions"
msgstr "" msgstr ""
#: lib/mv_web/live/member_live/index.html.heex:77 #: lib/mv_web/live/member_live/index.html.heex:77
#: lib/mv_web/live/user_live/index.html.heex:69 #: lib/mv_web/live/user_live/index.html.heex:70
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Are you sure?" msgid "Are you sure?"
msgstr "" msgstr ""
#: lib/mv_web/components/layouts.ex:71 #: lib/mv_web/components/layouts.ex:72
#: lib/mv_web/components/layouts.ex:83 #: lib/mv_web/components/layouts.ex:84
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Attempting to reconnect" msgid "Attempting to reconnect"
msgstr "" msgstr ""
@ -36,14 +36,14 @@ msgid "City"
msgstr "" msgstr ""
#: lib/mv_web/live/member_live/index.html.heex:79 #: lib/mv_web/live/member_live/index.html.heex:79
#: lib/mv_web/live/user_live/index.html.heex:71 #: lib/mv_web/live/user_live/index.html.heex:72
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Delete" msgid "Delete"
msgstr "" msgstr ""
#: lib/mv_web/live/member_live/index.html.heex:71 #: lib/mv_web/live/member_live/index.html.heex:71
#: lib/mv_web/live/user_live/form.ex:109 #: lib/mv_web/live/user_live/form.ex:156
#: lib/mv_web/live/user_live/index.html.heex:63 #: lib/mv_web/live/user_live/index.html.heex:64
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Edit" msgid "Edit"
msgstr "" msgstr ""
@ -58,7 +58,7 @@ msgstr ""
#: lib/mv_web/live/member_live/index.html.heex:58 #: 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/show.ex:27
#: lib/mv_web/live/user_live/form.ex:14 #: lib/mv_web/live/user_live/form.ex:14
#: lib/mv_web/live/user_live/index.html.heex:48 #: 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:24
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Email" msgid "Email"
@ -89,17 +89,17 @@ msgid "New Member"
msgstr "" msgstr ""
#: lib/mv_web/live/member_live/index.html.heex:68 #: lib/mv_web/live/member_live/index.html.heex:68
#: lib/mv_web/live/user_live/index.html.heex:60 #: lib/mv_web/live/user_live/index.html.heex:61
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Show" msgid "Show"
msgstr "" msgstr ""
#: lib/mv_web/components/layouts.ex:78 #: lib/mv_web/components/layouts.ex:79
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Something went wrong!" msgid "Something went wrong!"
msgstr "" msgstr ""
#: lib/mv_web/components/layouts.ex:66 #: lib/mv_web/components/layouts.ex:67
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "We can't find the internet" msgid "We can't find the internet"
msgstr "" msgstr ""
@ -168,7 +168,7 @@ msgstr ""
#: lib/mv_web/live/member_live/form.ex:49 #: lib/mv_web/live/member_live/form.ex:49
#: lib/mv_web/live/property_live/form.ex:41 #: lib/mv_web/live/property_live/form.ex:41
#: lib/mv_web/live/property_type_live/form.ex:29 #: lib/mv_web/live/property_type_live/form.ex:29
#: lib/mv_web/live/user_live/form.ex:92 #: lib/mv_web/live/user_live/form.ex:139
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Saving..." msgid "Saving..."
msgstr "" msgstr ""
@ -262,7 +262,7 @@ msgstr ""
#: lib/mv_web/live/member_live/form.ex:52 #: lib/mv_web/live/member_live/form.ex:52
#: lib/mv_web/live/property_live/form.ex:44 #: lib/mv_web/live/property_live/form.ex:44
#: lib/mv_web/live/property_type_live/form.ex:32 #: lib/mv_web/live/property_type_live/form.ex:32
#: lib/mv_web/live/user_live/form.ex:95 #: lib/mv_web/live/user_live/form.ex:142
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Cancel" msgid "Cancel"
msgstr "" msgstr ""
@ -287,7 +287,7 @@ msgstr ""
msgid "Edit User" msgid "Edit User"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/show.ex:27 #: lib/mv_web/live/user_live/show.ex:35
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Enabled" msgid "Enabled"
msgstr "" msgstr ""
@ -302,29 +302,81 @@ msgstr ""
msgid "Immutable" msgid "Immutable"
msgstr "" msgstr ""
#: lib/mv_web/components/layouts/navbar.ex:73 #: lib/mv_web/components/layouts/navbar.ex:75
#, elixir-autogen, elixir-format #, elixir-autogen
msgid "Logout" msgid "Logout"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/index.ex:12 #: lib/mv_web/components/layouts/navbar.ex:16
#: lib/mv_web/live/user_live/index.html.heex:3
#, elixir-autogen, elixir-format
msgid "Listing Users"
msgstr ""
#: lib/mv_web/live/property_live/form.ex:27
#, elixir-autogen, elixir-format
msgid "Member"
msgstr ""
#: lib/mv_web/components/layouts/navbar.ex:14
#: lib/mv_web/live/member_live/index.ex:12 #: lib/mv_web/live/member_live/index.ex:12
#: lib/mv_web/live/member_live/index.html.heex:3 #: lib/mv_web/live/member_live/index.html.heex:3
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Members" msgid "Members"
msgstr "" msgstr ""
#: lib/mv_web/live/property_live/form.ex:27
#: lib/mv_web/live/user_live/index.html.heex:52
#: lib/mv_web/live/user_live/show.ex:25
#, elixir-autogen, elixir-format
msgid "Member"
msgstr ""
#: lib/mv_web/live/user_live/index.html.heex:55
#, elixir-autogen
msgid "No member"
msgstr ""
#: lib/mv_web/live/user_live/show.ex:31
#, elixir-autogen
msgid "No member assigned"
msgstr ""
msgid "Profile"
msgstr ""
msgid "Edit your member information"
msgstr ""
msgid "Save Profile"
msgstr ""
msgid "Profile updated successfully"
msgstr ""
msgid "No member profile found"
msgstr ""
#: lib/mv_web/live/user_live/form.ex:18
#, elixir-autogen
msgid "Member Assignment"
msgstr ""
#: lib/mv_web/live/user_live/form.ex:31
#, elixir-autogen
msgid "Create new member automatically"
msgstr ""
#: lib/mv_web/live/user_live/form.ex:46
#, elixir-autogen
msgid "Assign to existing member"
msgstr ""
#: lib/mv_web/live/user_live/form.ex:54
#, elixir-autogen
msgid "Select Member"
msgstr ""
#: lib/mv_web/live/user_live/form.ex:57
#, elixir-autogen
msgid "Choose a member..."
msgstr ""
#: lib/mv_web/live/user_live/index.ex:15
#: lib/mv_web/live/user_live/index.html.heex:3
#, elixir-autogen, elixir-format
msgid "Listing Users"
msgstr ""
#: lib/mv_web/live/member_live/index.html.heex:50 #: lib/mv_web/live/member_live/index.html.heex:50
#: lib/mv_web/live/property_type_live/form.ex:16 #: lib/mv_web/live/property_type_live/form.ex:16
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
@ -336,29 +388,29 @@ msgstr ""
msgid "New User" msgid "New User"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/show.ex:27 #: lib/mv_web/live/user_live/show.ex:35
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Not enabled" msgid "Not enabled"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/show.ex:25 #: lib/mv_web/live/user_live/show.ex:33
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Not set" msgid "Not set"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:75 #: lib/mv_web/live/user_live/form.ex:122
#: lib/mv_web/live/user_live/form.ex:83 #: lib/mv_web/live/user_live/form.ex:130
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Note" msgid "Note"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/index.html.heex:56 #: lib/mv_web/live/user_live/index.html.heex:57
#: lib/mv_web/live/user_live/show.ex:25 #: lib/mv_web/live/user_live/show.ex:33
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "OIDC ID" msgid "OIDC ID"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/show.ex:26 #: lib/mv_web/live/user_live/show.ex:34
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Password Authentication" msgid "Password Authentication"
msgstr "" msgstr ""
@ -368,7 +420,7 @@ msgstr ""
msgid "Please select a property type first" msgid "Please select a property type first"
msgstr "" msgstr ""
#: lib/mv_web/components/layouts/navbar.ex:69 #: lib/mv_web/components/layouts/navbar.ex:71
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Profil" msgid "Profil"
msgstr "" msgstr ""
@ -413,17 +465,17 @@ msgstr ""
msgid "Select member" msgid "Select member"
msgstr "" msgstr ""
#: lib/mv_web/components/layouts/navbar.ex:72 #: lib/mv_web/components/layouts/navbar.ex:74
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Settings" msgid "Settings"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:93 #: lib/mv_web/live/user_live/form.ex:140
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Save User" msgid "Save User"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/show.ex:38 #: lib/mv_web/live/user_live/show.ex:50
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Show User" msgid "Show User"
msgstr "" msgstr ""
@ -453,7 +505,7 @@ msgstr ""
msgid "Use this form to manage user records in your database." msgid "Use this form to manage user records in your database."
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:110 #: lib/mv_web/live/user_live/form.ex:157
#: lib/mv_web/live/user_live/show.ex:9 #: lib/mv_web/live/user_live/show.ex:9
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "User" msgid "User"
@ -479,77 +531,77 @@ msgstr ""
msgid "descending" msgid "descending"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:109 #: lib/mv_web/live/user_live/form.ex:156
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "New" msgid "New"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:64 #: lib/mv_web/live/user_live/form.ex:111
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Admin Note" msgid "Admin Note"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:64 #: lib/mv_web/live/user_live/form.ex:111
#, elixir-autogen, elixir-format #, 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." msgid "As an administrator, you can directly set a new password for this user using the same secure Ash Authentication system."
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:55 #: lib/mv_web/live/user_live/form.ex:102
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "At least 8 characters" msgid "At least 8 characters"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:27 #: lib/mv_web/live/user_live/form.ex:74
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Change Password" msgid "Change Password"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:75 #: lib/mv_web/live/user_live/form.ex:122
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Check 'Change Password' above to set a new password for this user." msgid "Check 'Change Password' above to set a new password for this user."
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:45 #: lib/mv_web/live/user_live/form.ex:92
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Confirm Password" msgid "Confirm Password"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:57 #: lib/mv_web/live/user_live/form.ex:104
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Consider using special characters" msgid "Consider using special characters"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:56 #: lib/mv_web/live/user_live/form.ex:103
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Include both letters and numbers" msgid "Include both letters and numbers"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:35 #: lib/mv_web/live/user_live/form.ex:82
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Password" msgid "Password"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:53 #: lib/mv_web/live/user_live/form.ex:100
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Password requirements" msgid "Password requirements"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/index.html.heex:25 #: lib/mv_web/live/user_live/index.html.heex:21
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Select all users" msgid "Select all users"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/index.html.heex:39 #: lib/mv_web/live/user_live/index.html.heex:35
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Select user" msgid "Select user"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:27 #: lib/mv_web/live/user_live/form.ex:74
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Set Password" msgid "Set Password"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:83 #: lib/mv_web/live/user_live/form.ex:130
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "User will be created without a password. Check 'Set Password' to add one." msgid "User will be created without a password. Check 'Set Password' to add one."
msgstr "" msgstr ""

View file

@ -17,13 +17,13 @@ msgid "Actions"
msgstr "" msgstr ""
#: lib/mv_web/live/member_live/index.html.heex:77 #: lib/mv_web/live/member_live/index.html.heex:77
#: lib/mv_web/live/user_live/index.html.heex:69 #: lib/mv_web/live/user_live/index.html.heex:70
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Are you sure?" msgid "Are you sure?"
msgstr "" msgstr ""
#: lib/mv_web/components/layouts.ex:71 #: lib/mv_web/components/layouts.ex:72
#: lib/mv_web/components/layouts.ex:83 #: lib/mv_web/components/layouts.ex:84
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Attempting to reconnect" msgid "Attempting to reconnect"
msgstr "" msgstr ""
@ -36,14 +36,14 @@ msgid "City"
msgstr "" msgstr ""
#: lib/mv_web/live/member_live/index.html.heex:79 #: lib/mv_web/live/member_live/index.html.heex:79
#: lib/mv_web/live/user_live/index.html.heex:71 #: lib/mv_web/live/user_live/index.html.heex:72
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Delete" msgid "Delete"
msgstr "" msgstr ""
#: lib/mv_web/live/member_live/index.html.heex:71 #: lib/mv_web/live/member_live/index.html.heex:71
#: lib/mv_web/live/user_live/form.ex:109 #: lib/mv_web/live/user_live/form.ex:156
#: lib/mv_web/live/user_live/index.html.heex:63 #: lib/mv_web/live/user_live/index.html.heex:64
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Edit" msgid "Edit"
msgstr "" msgstr ""
@ -58,7 +58,7 @@ msgstr ""
#: lib/mv_web/live/member_live/index.html.heex:58 #: 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/show.ex:27
#: lib/mv_web/live/user_live/form.ex:14 #: lib/mv_web/live/user_live/form.ex:14
#: lib/mv_web/live/user_live/index.html.heex:48 #: 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:24
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Email" msgid "Email"
@ -89,17 +89,17 @@ msgid "New Member"
msgstr "" msgstr ""
#: lib/mv_web/live/member_live/index.html.heex:68 #: lib/mv_web/live/member_live/index.html.heex:68
#: lib/mv_web/live/user_live/index.html.heex:60 #: lib/mv_web/live/user_live/index.html.heex:61
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Show" msgid "Show"
msgstr "" msgstr ""
#: lib/mv_web/components/layouts.ex:78 #: lib/mv_web/components/layouts.ex:79
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Something went wrong!" msgid "Something went wrong!"
msgstr "" msgstr ""
#: lib/mv_web/components/layouts.ex:66 #: lib/mv_web/components/layouts.ex:67
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "We can't find the internet" msgid "We can't find the internet"
msgstr "" msgstr ""
@ -168,7 +168,7 @@ msgstr ""
#: lib/mv_web/live/member_live/form.ex:49 #: lib/mv_web/live/member_live/form.ex:49
#: lib/mv_web/live/property_live/form.ex:41 #: lib/mv_web/live/property_live/form.ex:41
#: lib/mv_web/live/property_type_live/form.ex:29 #: lib/mv_web/live/property_type_live/form.ex:29
#: lib/mv_web/live/user_live/form.ex:92 #: lib/mv_web/live/user_live/form.ex:139
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Saving..." msgid "Saving..."
msgstr "" msgstr ""
@ -262,7 +262,7 @@ msgstr ""
#: lib/mv_web/live/member_live/form.ex:52 #: lib/mv_web/live/member_live/form.ex:52
#: lib/mv_web/live/property_live/form.ex:44 #: lib/mv_web/live/property_live/form.ex:44
#: lib/mv_web/live/property_type_live/form.ex:32 #: lib/mv_web/live/property_type_live/form.ex:32
#: lib/mv_web/live/user_live/form.ex:95 #: lib/mv_web/live/user_live/form.ex:142
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Cancel" msgid "Cancel"
msgstr "" msgstr ""
@ -287,7 +287,7 @@ msgstr ""
msgid "Edit User" msgid "Edit User"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/show.ex:27 #: lib/mv_web/live/user_live/show.ex:35
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Enabled" msgid "Enabled"
msgstr "" msgstr ""
@ -302,27 +302,81 @@ msgstr ""
msgid "Immutable" msgid "Immutable"
msgstr "" msgstr ""
#: lib/mv_web/components/layouts/navbar.ex:73 #: lib/mv_web/components/layouts/navbar.ex:75
#, elixir-autogen, elixir-format #, elixir-autogen
msgid "Logout" msgid "Logout"
msgstr "" msgstr "Logout"
#: lib/mv_web/live/user_live/index.ex:12 #: lib/mv_web/components/layouts/navbar.ex:16
#: lib/mv_web/live/user_live/index.html.heex:3
#, elixir-autogen, elixir-format, fuzzy
msgid "Listing Users"
msgstr ""
#: lib/mv_web/live/property_live/form.ex:27
#, elixir-autogen, elixir-format
msgid "Member"
msgstr ""
#: lib/mv_web/components/layouts/navbar.ex:14
#: lib/mv_web/live/member_live/index.ex:12 #: lib/mv_web/live/member_live/index.ex:12
#: lib/mv_web/live/member_live/index.html.heex:3 #: lib/mv_web/live/member_live/index.html.heex:3
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Members" msgid "Members"
msgstr "Members"
#: lib/mv_web/live/property_live/form.ex:27
#: lib/mv_web/live/user_live/index.html.heex:52
#: lib/mv_web/live/user_live/show.ex:25
#, elixir-autogen, elixir-format
msgid "Member"
msgstr "Member"
#: lib/mv_web/live/user_live/index.html.heex:55
#, elixir-autogen
msgid "No member"
msgstr ""
#: lib/mv_web/live/user_live/show.ex:31
#, elixir-autogen
msgid "No member assigned"
msgstr ""
#, fuzzy
msgid "Profile"
msgstr "Profile"
msgid "Edit your member information"
msgstr ""
#, fuzzy
msgid "Save Profile"
msgstr "Profile"
msgid "Profile updated successfully"
msgstr ""
msgid "No member profile found"
msgstr "No member profile found"
#: lib/mv_web/live/user_live/form.ex:18
#, elixir-autogen
msgid "Member Assignment"
msgstr "Member Assignment"
#: lib/mv_web/live/user_live/form.ex:31
#, elixir-autogen
msgid "Create new member automatically"
msgstr "Create new member automatically"
#: lib/mv_web/live/user_live/form.ex:46
#, elixir-autogen
msgid "Assign to existing member"
msgstr "Assign to existing member"
#: lib/mv_web/live/user_live/form.ex:54
#, elixir-autogen
msgid "Select Member"
msgstr "Select Member"
#: lib/mv_web/live/user_live/form.ex:57
#, elixir-autogen
msgid "Choose a member..."
msgstr "Choose a member..."
#: lib/mv_web/live/user_live/index.ex:15
#: lib/mv_web/live/user_live/index.html.heex:3
#, elixir-autogen, elixir-format, fuzzy
msgid "Listing Users"
msgstr "" msgstr ""
#: lib/mv_web/live/member_live/index.html.heex:50 #: lib/mv_web/live/member_live/index.html.heex:50
@ -336,29 +390,29 @@ msgstr ""
msgid "New User" msgid "New User"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/show.ex:27 #: lib/mv_web/live/user_live/show.ex:35
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Not enabled" msgid "Not enabled"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/show.ex:25 #: lib/mv_web/live/user_live/show.ex:33
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Not set" msgid "Not set"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:75 #: lib/mv_web/live/user_live/form.ex:122
#: lib/mv_web/live/user_live/form.ex:83 #: lib/mv_web/live/user_live/form.ex:130
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Note" msgid "Note"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/index.html.heex:56 #: lib/mv_web/live/user_live/index.html.heex:57
#: lib/mv_web/live/user_live/show.ex:25 #: lib/mv_web/live/user_live/show.ex:33
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "OIDC ID" msgid "OIDC ID"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/show.ex:26 #: lib/mv_web/live/user_live/show.ex:34
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Password Authentication" msgid "Password Authentication"
msgstr "" msgstr ""
@ -368,10 +422,10 @@ msgstr ""
msgid "Please select a property type first" msgid "Please select a property type first"
msgstr "" msgstr ""
#: lib/mv_web/components/layouts/navbar.ex:69 #: lib/mv_web/components/layouts/navbar.ex:71
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Profil" msgid "Profil"
msgstr "" msgstr "Profile"
#: lib/mv_web/live/property_live/form.ex:207 #: lib/mv_web/live/property_live/form.ex:207
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
@ -411,19 +465,19 @@ msgstr ""
#: lib/mv_web/live/member_live/index.html.heex:41 #: lib/mv_web/live/member_live/index.html.heex:41
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Select member" msgid "Select member"
msgstr "" msgstr "Select Member"
#: lib/mv_web/components/layouts/navbar.ex:72 #: lib/mv_web/components/layouts/navbar.ex:74
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Settings" msgid "Settings"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:93 #: lib/mv_web/live/user_live/form.ex:140
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Save User" msgid "Save User"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/show.ex:38 #: lib/mv_web/live/user_live/show.ex:50
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Show User" msgid "Show User"
msgstr "" msgstr ""
@ -453,7 +507,7 @@ msgstr ""
msgid "Use this form to manage user records in your database." msgid "Use this form to manage user records in your database."
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:110 #: lib/mv_web/live/user_live/form.ex:157
#: lib/mv_web/live/user_live/show.ex:9 #: lib/mv_web/live/user_live/show.ex:9
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "User" msgid "User"
@ -479,77 +533,77 @@ msgstr ""
msgid "descending" msgid "descending"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:109 #: lib/mv_web/live/user_live/form.ex:156
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "New" msgid "New"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:64 #: lib/mv_web/live/user_live/form.ex:111
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Admin Note" msgid "Admin Note"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:64 #: lib/mv_web/live/user_live/form.ex:111
#, elixir-autogen, elixir-format #, 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." msgid "As an administrator, you can directly set a new password for this user using the same secure Ash Authentication system."
msgstr "As an administrator, you can directly set a new password for this user using the same secure Ash Authentication system." msgstr "As an administrator, you can directly set a new password for this user using the same secure Ash Authentication system."
#: lib/mv_web/live/user_live/form.ex:55 #: lib/mv_web/live/user_live/form.ex:102
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "At least 8 characters" msgid "At least 8 characters"
msgstr "At least 8 characters" msgstr "At least 8 characters"
#: lib/mv_web/live/user_live/form.ex:27 #: lib/mv_web/live/user_live/form.ex:74
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Change Password" msgid "Change Password"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:75 #: lib/mv_web/live/user_live/form.ex:122
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Check 'Change Password' above to set a new password for this user." msgid "Check 'Change Password' above to set a new password for this user."
msgstr "Check 'Change Password' above to set a new password for this user." msgstr "Check 'Change Password' above to set a new password for this user."
#: lib/mv_web/live/user_live/form.ex:45 #: lib/mv_web/live/user_live/form.ex:92
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Confirm Password" msgid "Confirm Password"
msgstr "Confirm Password" msgstr "Confirm Password"
#: lib/mv_web/live/user_live/form.ex:57 #: lib/mv_web/live/user_live/form.ex:104
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Consider using special characters" msgid "Consider using special characters"
msgstr "Consider using special characters" msgstr "Consider using special characters"
#: lib/mv_web/live/user_live/form.ex:56 #: lib/mv_web/live/user_live/form.ex:103
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Include both letters and numbers" msgid "Include both letters and numbers"
msgstr "Include both letters and numbers" msgstr "Include both letters and numbers"
#: lib/mv_web/live/user_live/form.ex:35 #: lib/mv_web/live/user_live/form.ex:82
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Password" msgid "Password"
msgstr "Password" msgstr "Password"
#: lib/mv_web/live/user_live/form.ex:53 #: lib/mv_web/live/user_live/form.ex:100
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Password requirements" msgid "Password requirements"
msgstr "Password requirements" msgstr "Password requirements"
#: lib/mv_web/live/user_live/index.html.heex:25 #: lib/mv_web/live/user_live/index.html.heex:21
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Select all users" msgid "Select all users"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/index.html.heex:39 #: lib/mv_web/live/user_live/index.html.heex:35
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Select user" msgid "Select user"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:27 #: lib/mv_web/live/user_live/form.ex:74
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Set Password" msgid "Set Password"
msgstr "Set Password" msgstr "Set Password"
#: lib/mv_web/live/user_live/form.ex:83 #: lib/mv_web/live/user_live/form.ex:130
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "User will be created without a password. Check 'Set Password' to add one." 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." msgstr "User will be created without a password. Check 'Set Password' to add one."

View file

@ -0,0 +1,17 @@
defmodule Mv.Repo.Migrations.AddUniqueMemberId 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
create unique_index(:users, [:member_id], name: "users_unique_member_id_index")
end
def down do
drop_if_exists unique_index(:users, [:member_id], name: "users_unique_member_id_index")
end
end

View file

@ -0,0 +1,21 @@
defmodule Mv.Repo.Migrations.AddAdminToUsers 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
alter table(:users) do
add :admin?, :boolean, null: false, default: false
end
end
def down do
alter table(:users) do
remove :admin?
end
end
end

View 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": "5D2170BB579B2CE45596ED1CED44D45A3BBEDDB7B9573CB7E5F087F47F4C4CA9",
"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_id_index",
"keys": [
{
"type": "atom",
"value": "member_id"
}
],
"name": "unique_member_id",
"nils_distinct?": true,
"where": null
}
],
"multitenancy": {
"attribute": null,
"global": null,
"strategy": null
},
"repo": "Elixir.Mv.Repo",
"schema": null,
"table": "users"
}

View file

@ -0,0 +1,153 @@
{
"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?": false,
"default": "false",
"generated?": false,
"precision": null,
"primary_key?": false,
"references": null,
"scale": null,
"size": null,
"source": "admin?",
"type": "boolean"
},
{
"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": "045ACBFE7FEA8E44855F93AE816A54CE6302371FEA43C8865288B86E6F908838",
"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_id_index",
"keys": [
{
"type": "atom",
"value": "member_id"
}
],
"name": "unique_member_id",
"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"
}

View file

@ -0,0 +1,107 @@
defmodule Mv.Accounts.RegistrationMemberTest do
use Mv.DataCase
alias Mv.Accounts
alias Mv.Accounts.User.MemberCreationNotifier
describe "registration creates member" do
test "registering a user creates and assigns a new member via notifier" do
# Create user first
assert {:ok, user} =
Accounts.register_with_password(%{
email: "test@example.com",
password: "password123"
})
assert to_string(user.email) == "test@example.com"
# Manually trigger the notifier to test synchronously
notification = %Ash.Notifier.Notification{
action: %{name: :register_with_password},
resource: Mv.Accounts.User,
data: user
}
assert :ok = MemberCreationNotifier.notify(notification)
# Reload user to get updated member_id
user = Ash.reload!(user, domain: Mv.Accounts)
# User should have a member assigned
assert user.member_id != nil
# Member should exist and have correct data
member = Ash.get!(Mv.Membership.Member, user.member_id, domain: Mv.Membership)
assert member.email == "test@example.com"
assert member.first_name == "User"
assert member.last_name == "Generated"
end
test "notifier doesn't create member if user already has one" do
# Create a member first
{:ok, existing_member} =
Mv.Membership.create_member(%{
email: "existing@example.com",
first_name: "Existing",
last_name: "Member"
})
# Create user with existing member
assert {:ok, user} =
Accounts.create_user(%{
email: "existing@example.com",
member_id: existing_member.id
})
# Trigger notifier
notification = %Ash.Notifier.Notification{
action: %{name: :create_user},
resource: Mv.Accounts.User,
data: user
}
assert :ok = MemberCreationNotifier.notify(notification)
# User should still have the same member
user = Ash.reload!(user, domain: Mv.Accounts)
assert user.member_id == existing_member.id
# Should not have created a new member
member = Ash.get!(Mv.Membership.Member, user.member_id, domain: Mv.Membership)
assert member.first_name == "Existing"
assert member.last_name == "Member"
end
test "member data has correct default values" do
# Create user
assert {:ok, user} =
Accounts.create_user(%{
email: "member@example.com"
})
# Trigger notifier manually
notification = %Ash.Notifier.Notification{
action: %{name: :create_user},
resource: Mv.Accounts.User,
data: user
}
assert :ok = MemberCreationNotifier.notify(notification)
# Reload and check member
user = Ash.reload!(user, domain: Mv.Accounts)
member = Ash.get!(Mv.Membership.Member, user.member_id, domain: Mv.Membership)
# Email should match user
assert member.email == "member@example.com"
# Other fields should have default values
assert member.first_name == "User"
assert member.last_name == "Generated"
# Allow both false and nil
assert member.paid == false || member.paid == nil
assert member.phone_number == nil
assert member.birth_date == nil
end
end
end

View file

@ -0,0 +1,20 @@
defmodule Mv.Accounts.UserDeleteMemberTest do
use Mv.DataCase, async: true
alias Mv.Accounts
alias Mv.Membership
describe "user deletion does not delete member" do
test "deleting a user keeps the member in the system" do
{:ok, member} =
Membership.create_member(%{
first_name: "Keep",
last_name: "Me",
email: "keepme@example.com"
})
{:ok, user} = Accounts.create_user(%{email: "keepuser@example.com", member_id: member.id})
:ok = Accounts.destroy_user(user)
assert Membership.get_member!(member.id)
end
end
end

View file

@ -0,0 +1,77 @@
defmodule Mv.Accounts.UserMemberIntegrationTest do
use Mv.DataCase, async: true
alias Mv.Accounts
alias Mv.Membership
alias Mv.Accounts.User.MemberCreationNotifier
describe "User-Member-Relation" do
test "ein User kann einem Member zugeordnet werden" do
{:ok, member} =
Membership.create_member(%{
first_name: "Max",
last_name: "Mustermann",
email: "max@example.com"
})
{:ok, user} = Accounts.create_user(%{email: "user1@example.com", member_id: member.id})
assert user.member_id == member.id
end
test "ein Member kann nur einem User zugeordnet werden (unique constraint)" do
{:ok, member} =
Membership.create_member(%{
first_name: "Anna",
last_name: "Test",
email: "anna@example.com"
})
{:ok, user1} = Accounts.create_user(%{email: "user2@example.com", member_id: member.id})
assert user1.member_id == member.id
{:error, %Ash.Error.Invalid{errors: errors}} =
Accounts.create_user(%{email: "user3@example.com", member_id: member.id})
assert Enum.any?(errors, fn error ->
error.message =~ "already been taken" or error.field == :member_id
end)
end
test "ein User ohne Member ist nicht erlaubt (bei Registrierung/Erstellung)" do
# Create user without member first
result = Accounts.create_user(%{email: "user4@example.com"})
case result do
{:ok, user} ->
# User is created but doesn't have member yet
assert user.member_id == nil
# Manually trigger the notifier to simulate automatic member creation
notification = %Ash.Notifier.Notification{
action: %{name: :create_user},
resource: Mv.Accounts.User,
data: user
}
assert :ok = MemberCreationNotifier.notify(notification)
# Reload user and verify member was created and assigned
user = Ash.reload!(user, domain: Mv.Accounts)
assert user.member_id, "User should have a member_id assigned after notifier"
{:error, _} ->
flunk("User creation should succeed")
end
end
test "ein Member kann ohne User existieren" do
{:ok, member} =
Membership.create_member(%{
first_name: "Lisa",
last_name: "Solo",
email: "lisa@example.com"
})
assert member.id
end
end
end

View file

@ -119,8 +119,10 @@ defmodule MvWeb.OidcIntegrationTest do
assert {:error, %Ash.Error.Invalid{errors: errors}} = result assert {:error, %Ash.Error.Invalid{errors: errors}} = result
# Check for either Required error or InvalidAttribute error with email field
assert Enum.any?(errors, fn err -> assert Enum.any?(errors, fn err ->
match?(%Ash.Error.Changes.Required{field: :email}, err) match?(%Ash.Error.Changes.Required{field: :email}, err) or
match?(%Ash.Error.Changes.InvalidAttribute{field: :email}, err)
end) end)
end end

View file

@ -0,0 +1,81 @@
defmodule MvWeb.UserLive.AdminMemberAssignmentTest do
use MvWeb.ConnCase, async: true
import Phoenix.LiveViewTest
test "admin can assign existing member without user to new user", %{conn: conn} do
{:ok, member} =
Mv.Membership.create_member(%{
first_name: "Admin",
last_name: "Choice",
email: "adminchoice@example.com"
})
conn = conn_with_oidc_user(conn, %{email: "admin@example.com"})
{:ok, view, _html} = live(conn, "/users/new")
# First click the "assign existing member" radio button
view
|> element("input[value='assign_existing']")
|> render_click()
# Then submit the form with member_id
view
|> form("#user-form", user: %{email: "adminuser@example.com", member_id: member.id})
|> render_submit()
user =
Ash.get!(Mv.Accounts.User, [email: Ash.CiString.new("adminuser@example.com")],
domain: Mv.Accounts
)
assert user.member_id == member.id
end
test "admin can create new member for user if none selected", %{conn: conn} do
conn = conn_with_oidc_user(conn, %{email: "admin2@example.com"})
{:ok, view, _html} = live(conn, "/users/new")
# Default mode is "create_new", so just submit
view
|> form("#user-form", user: %{email: "adminnewmember@example.com"})
|> render_submit()
user =
Ash.get!(Mv.Accounts.User, [email: Ash.CiString.new("adminnewmember@example.com")],
domain: Mv.Accounts
)
assert user.member_id
{:ok, member} = Mv.Membership.get_member!(user.member_id)
assert member.email == "adminnewmember@example.com"
end
test "admin cannot assign member that already has a user", %{conn: conn} do
{:ok, member} =
Mv.Membership.create_member(%{
first_name: "Taken",
last_name: "Member",
email: "taken@example.com"
})
{:ok, _user} =
Mv.Accounts.create_user(%{email: "takenuser@example.com", member_id: member.id})
conn = conn_with_oidc_user(conn, %{email: "admin3@example.com"})
{:ok, view, _html} = live(conn, "/users/new")
# First click the "assign existing member" radio button
view
|> element("input[value='assign_existing']")
|> render_click()
# Check that the already assigned member is NOT in the dropdown options
html = render(view)
# Member should not be available for selection
refute html =~ "Taken Member"
# The dropdown should have limited or no options since member is already taken
# Placeholder should be visible
assert html =~ "Choose a member..."
end
end

View file

@ -244,7 +244,9 @@ defmodule MvWeb.UserLive.FormTest do
flunk("Expected validation error but form was submitted successfully") flunk("Expected validation error but form was submitted successfully")
html when is_binary(html) -> html when is_binary(html) ->
assert html =~ "must have length of at least 8" # Allow for different validation message sources
assert html =~ "length must be greater than or equal to 8" or
html =~ "must have length of at least 8"
end end
end end
end end

View file

@ -0,0 +1,36 @@
defmodule MvWeb.UserLive.MemberDisplayTest do
use MvWeb.ConnCase, async: true
import Phoenix.LiveViewTest
test "User-Liste zeigt zugeordneten Member an", %{conn: conn} do
{:ok, member} =
Mv.Membership.create_member(%{
first_name: "Max",
last_name: "Mustermann",
email: "max@example.com"
})
{:ok, _user} = Mv.Accounts.create_user(%{email: "user5@example.com", member_id: member.id})
conn = conn_with_oidc_user(conn)
{:ok, _view, html} = live(conn, "/users")
assert html =~ "Max Mustermann"
# User email, not member email
assert html =~ "user5@example.com"
end
test "User-Detailansicht zeigt Member-Daten an", %{conn: conn} do
{:ok, member} =
Mv.Membership.create_member(%{
first_name: "Anna",
last_name: "Test",
email: "anna@example.com"
})
{:ok, user} = Mv.Accounts.create_user(%{email: "user6@example.com", member_id: member.id})
conn = conn_with_oidc_user(conn)
{:ok, _view, html} = live(conn, "/users/#{user.id}")
assert html =~ "Anna Test"
# In detail view, member email should be shown
assert html =~ "anna@example.com"
end
end