@@ -999,11 +789,6 @@ defmodule MvWeb.CoreComponents do
>
Kernel.++(["focus-visible:ring-inset"])
- |> Enum.join(" ")
-
- "flex items-center gap-2 px-2 py-1 rounded cursor-pointer hover:bg-base-200 w-full text-left #{focus}"
- end
-
@impl true
def mount(socket) do
{:ok, assign(socket, :open, false)}
@@ -69,7 +59,7 @@ defmodule MvWeb.Components.ExportDropdown do
@@ -85,7 +75,7 @@ defmodule MvWeb.Components.ExportDropdown do
diff --git a/lib/mv_web/helpers/membership_fee_helpers.ex b/lib/mv_web/helpers/membership_fee_helpers.ex
index e8a2ce8..27c99f5 100644
--- a/lib/mv_web/helpers/membership_fee_helpers.ex
+++ b/lib/mv_web/helpers/membership_fee_helpers.ex
@@ -219,17 +219,6 @@ defmodule MvWeb.Helpers.MembershipFeeHelpers do
def status_color(:unpaid), do: "badge-error"
def status_color(:suspended), do: "badge-ghost"
- @doc """
- Returns the Core Components badge variant for a cycle status (WCAG-compliant).
-
- Use with <.badge variant={MembershipFeeHelpers.status_variant(status)}>.
- Suspended uses :warning (yellow) to match the edit cycle-status button.
- """
- @spec status_variant(:paid | :unpaid | :suspended) :: :success | :error | :warning
- def status_variant(:paid), do: :success
- def status_variant(:unpaid), do: :error
- def status_variant(:suspended), do: :warning
-
@doc """
Gets the icon name for a status.
diff --git a/lib/mv_web/live/components/member_filter_component.ex b/lib/mv_web/live/components/member_filter_component.ex
index 4a42bbc..4ee72d3 100644
--- a/lib/mv_web/live/components/member_filter_component.ex
+++ b/lib/mv_web/live/components/member_filter_component.ex
@@ -58,9 +58,8 @@ defmodule MvWeb.Components.MemberFilterComponent do
def render(assigns) do
~H"""
- <.badge
+
0}
- variant="primary"
- size="sm"
+ class="badge badge-primary badge-sm"
>
{active_boolean_filters_count(@boolean_filters)}
-
- <.badge
+
+
0) &&
active_boolean_filters_count(@boolean_filters) == 0
}
- variant="primary"
- size="sm"
+ class="badge badge-primary badge-sm"
>
{@member_count}
-
+
-
-
-
-
- {if @user, do: gettext("Change Password"), else: gettext("Set Password")}
-
-
+
+
+
+
+ {if @user, do: gettext("Change Password"), else: gettext("Set Password")}
+
+
- <%= if @show_password_fields do %>
-
- <%= if @user && MvWeb.Helpers.UserHelpers.has_oidc?(@user) do %>
-
-
- {gettext("SSO / OIDC user")}
-
-
- {gettext(
- "This user is linked via SSO (Single Sign-On). A password set or changed here only affects login with email and password in this application. It does not change the password in your identity provider (e.g. Authentik). To change the SSO password, use the identity provider or your organization's IT."
- )}
-
-
- <% end %>
+ <%= if @show_password_fields do %>
+
+ <%= if @user && MvWeb.Helpers.UserHelpers.has_oidc?(@user) do %>
+
+
+ {gettext("SSO / OIDC user")}
+
+
+ {gettext(
+ "This user is linked via SSO (Single Sign-On). A password set or changed here only affects login with email and password in this application. It does not change the password in your identity provider (e.g. Authentik). To change the SSO password, use the identity provider or your organization's IT."
+ )}
+
+
+ <% end %>
+ <.input
+ field={@form[:password]}
+ label={gettext("Password")}
+ type="password"
+ required
+ autocomplete="new-password"
+ />
+
+
+ <%= if !@user do %>
<.input
- field={@form[:password]}
- label={gettext("Password")}
+ field={@form[:password_confirmation]}
+ label={gettext("Confirm Password")}
type="password"
required
autocomplete="new-password"
/>
-
-
- <%= if !@user do %>
- <.input
- field={@form[:password_confirmation]}
- label={gettext("Confirm Password")}
- type="password"
- required
- autocomplete="new-password"
- />
- <% end %>
-
-
-
{gettext("Password requirements")}:
-
- {gettext("At least 8 characters")}
- {gettext("Include both letters and numbers")}
- {gettext("Consider using special characters")}
-
-
-
- <%= if @user && @can_manage_member_linking do %>
-
-
- {gettext("Admin Note")}: {gettext(
- "As an administrator, you can directly set a new password for this user using the same secure Ash Authentication system."
- )}
-
-
- <% end %>
-
- <% else %>
- <%= if @user do %>
-
-
- {gettext("Note")}: {gettext(
- "Check 'Change Password' above to set a new password for this user."
- )}
-
-
- <% else %>
-
-
- {gettext("Note")}: {gettext(
- "User will be created without a password. Check 'Set Password' to add one."
- )}
-
-
<% end %>
- <% end %>
-
-
-
- <%= if @can_manage_member_linking do %>
-
-
{gettext("Linked Member")}
- <%= if @user && @user.member && !@unlink_member do %>
-
-
-
-
-
- {MvWeb.Helpers.MemberHelpers.display_name(@user.member)}
-
-
{@user.member.email}
-
- <.button
- type="button"
- variant="danger"
- size="sm"
- phx-click="unlink_member"
- >
- {gettext("Unlink Member")}
-
-
-
- <% else %>
- <%= if @unlink_member do %>
-
-
-
- {gettext("Unlinking scheduled")}: {gettext(
- "Member will be unlinked when you save. Cannot select new member until saved."
- )}
-
-
- <% end %>
-
-
-
-
+
+
{gettext("Password requirements")}:
+
+ {gettext("At least 8 characters")}
+ {gettext("Include both letters and numbers")}
+ {gettext("Consider using special characters")}
+
+
- <%= if length(@available_members) > 0 do %>
-
- <%= for {member, index} <- Enum.with_index(@available_members) do %>
-
-
- {MvWeb.Helpers.MemberHelpers.display_name(member)}
-
-
{member.email}
-
- <% end %>
-
- <% end %>
-
-
- <%= if @user && @user.email && @available_members != [] && Enum.all?(@available_members, &(&1.email == to_string(@user.email))) do %>
-
-
- {gettext("Note")}: {gettext(
- "A member with this email already exists. To link with a different member, please change one of the email addresses first."
- )}
-
-
- <% end %>
-
- <%= if @selected_member_id && @selected_member_name do %>
-
-
- {gettext("Selected")}: {@selected_member_name}
-
-
- {gettext("Save to confirm linking.")}
-
-
- <% end %>
+ <%= if @user && @can_manage_member_linking do %>
+
+
+ {gettext("Admin Note")}: {gettext(
+ "As an administrator, you can directly set a new password for this user using the same secure Ash Authentication system."
+ )}
+
<% end %>
- <% end %>
-
- <%!-- Danger zone: canonical pattern (same as member form) --%>
- <%= if @user && can?(@current_user, :destroy, @user) && !Mv.Helpers.SystemActor.system_user?(@user) do %>
-
-
- {gettext("Danger zone")}
-
-
-
- {gettext(
- "Deleting this user cannot be undone. The user account and any linked member association will be affected."
+ <% else %>
+ <%= if @user do %>
+
+
+ {gettext("Note")}: {gettext(
+ "Check 'Change Password' above to set a new password for this user."
)}
- <.button
- id="delete-user-form-trigger"
- type="button"
- variant="danger"
- phx-click="open_delete_modal"
- data-testid="user-delete"
- aria-label={gettext("Delete user %{email}", email: @user.email)}
- >
- <.icon name="hero-trash" class="size-4" />
- {gettext("Delete user")}
-
-
- <% end %>
-
- <%!-- Delete User Confirmation Modal (WCAG: focus in modal, keyboard confirm/cancel) --%>
- <%= if @user && assigns[:show_delete_modal] do %>
-
-
-
- {gettext("Delete User")}
-
-
- {gettext(
- "Are you sure you want to delete the user %{email}? This action cannot be undone.",
- email: @user.email
+ <% else %>
+
+
+ {gettext("Note")}: {gettext(
+ "User will be created without a password. Check 'Set Password' to add one."
)}
-
- <.button
- type="button"
- variant="neutral"
- phx-click="cancel_delete_modal"
- phx-mounted={JS.focus()}
- id="delete-user-form-modal-cancel"
- aria-label={gettext("Cancel")}
- >
- {gettext("Cancel")}
-
+
+ <% end %>
+ <% end %>
+
+
+
+ <%= if @can_manage_member_linking do %>
+
+
{gettext("Linked Member")}
+
+ <%= if @user && @user.member && !@unlink_member do %>
+
+
+
+
+
+ {MvWeb.Helpers.MemberHelpers.display_name(@user.member)}
+
+
{@user.member.email}
+
<.button
type="button"
variant="danger"
- phx-click={JS.push("delete", value: %{id: @user.id})}
- aria-label={gettext("Delete user")}
+ size="sm"
+ phx-click="unlink_member"
>
- {gettext("Delete")}
+ {gettext("Unlink Member")}
-
- <% end %>
+ <% else %>
+ <%= if @unlink_member do %>
+
+
+
+ {gettext("Unlinking scheduled")}: {gettext(
+ "Member will be unlinked when you save. Cannot select new member until saved."
+ )}
+
+
+ <% end %>
+
+
+
+
-
- <.button navigate={return_path(@return_to, @user)} variant="neutral">
- {gettext("Cancel")}
-
- <.button phx-disable-with={gettext("Saving...")} variant="primary">
- {gettext("Save User")}
-
+ <%= if length(@available_members) > 0 do %>
+
+ <%= for {member, index} <- Enum.with_index(@available_members) do %>
+
+
+ {MvWeb.Helpers.MemberHelpers.display_name(member)}
+
+
{member.email}
+
+ <% end %>
+
+ <% end %>
+
+
+ <%= if @user && @user.email && @available_members != [] && Enum.all?(@available_members, &(&1.email == to_string(@user.email))) do %>
+
+
+ {gettext("Note")}: {gettext(
+ "A member with this email already exists. To link with a different member, please change one of the email addresses first."
+ )}
+
+
+ <% end %>
+
+ <%= if @selected_member_id && @selected_member_name do %>
+
+
+ {gettext("Selected")}: {@selected_member_name}
+
+
+ {gettext("Save to confirm linking.")}
+
+
+ <% end %>
+
+ <% end %>
-
-
+ <% end %>
+
+ <%!-- Danger zone: canonical pattern (same as member form) --%>
+ <%= if @user && can?(@current_user, :destroy, @user) && !Mv.Helpers.SystemActor.system_user?(@user) do %>
+
+
+ {gettext("Danger zone")}
+
+
+
+ {gettext(
+ "Deleting this user cannot be undone. The user account and any linked member association will be affected."
+ )}
+
+ <.button
+ type="button"
+ variant="danger"
+ phx-click="delete"
+ phx-value-id={@user.id}
+ data-confirm={
+ gettext(
+ "Are you sure you want to delete the user %{email}? This action cannot be undone.",
+ email: @user.email
+ )
+ }
+ data-testid="user-delete"
+ aria-label={gettext("Delete user %{email}", email: @user.email)}
+ >
+ <.icon name="hero-trash" class="size-4" />
+ {gettext("Delete user")}
+
+
+
+ <% end %>
+
+
+ <.button navigate={return_path(@return_to, @user)} variant="neutral">
+ {gettext("Cancel")}
+
+ <.button phx-disable-with={gettext("Saving...")} variant="primary">
+ {gettext("Save User")}
+
+
+
"""
end
@@ -441,7 +399,6 @@ defmodule MvWeb.UserLive.Form do
|> assign(:selected_member_name, nil)
|> assign(:unlink_member, false)
|> assign(:focused_member_index, nil)
- |> assign_new(:show_delete_modal, fn -> false end)
|> load_initial_members()
|> assign_form()}
end
@@ -497,32 +454,6 @@ defmodule MvWeb.UserLive.Form do
end
end
- @impl true
- def handle_event("open_delete_modal", _params, socket) do
- {:noreply, assign(socket, :show_delete_modal, true)}
- end
-
- @impl true
- def handle_event("cancel_delete_modal", _params, socket) do
- {:noreply, close_delete_modal_and_restore_focus(socket)}
- end
-
- def handle_event("dialog_keydown", %{"key" => key}, socket) when key in ["Escape", "Esc"] do
- {:noreply, close_delete_modal_and_restore_focus(socket)}
- end
-
- def handle_event("dialog_keydown", _params, socket), do: {:noreply, socket}
-
- def handle_event("window_keydown", %{"key" => key}, socket) when key in ["Escape", "Esc"] do
- if socket.assigns[:show_delete_modal] do
- {:noreply, close_delete_modal_and_restore_focus(socket)}
- else
- {:noreply, socket}
- end
- end
-
- def handle_event("window_keydown", _params, socket), do: {:noreply, socket}
-
@impl true
def handle_event("delete", %{"id" => id}, socket) do
user = socket.assigns.user
@@ -530,22 +461,13 @@ defmodule MvWeb.UserLive.Form do
cond do
is_nil(user) ->
- {:noreply,
- socket
- |> put_flash(:error, gettext("User not found"))
- |> assign(:show_delete_modal, false)}
+ {:noreply, put_flash(socket, :error, gettext("User not found"))}
to_string(id) != to_string(user.id) ->
- {:noreply,
- socket
- |> put_flash(:error, gettext("User not found"))
- |> assign(:show_delete_modal, false)}
+ {:noreply, put_flash(socket, :error, gettext("User not found"))}
Mv.Helpers.SystemActor.system_user?(user) ->
- {:noreply,
- socket
- |> put_flash(:error, gettext("System user cannot be deleted."))
- |> assign(:show_delete_modal, false)}
+ {:noreply, put_flash(socket, :error, gettext("System user cannot be deleted."))}
true ->
handle_user_delete_destroy(socket, user, actor)
@@ -672,24 +594,13 @@ defmodule MvWeb.UserLive.Form do
{:error, %Ash.Error.Forbidden{}} ->
{:noreply,
- socket
- |> put_flash(:error, gettext("You do not have permission to delete this user"))
- |> assign(:show_delete_modal, false)}
+ put_flash(socket, :error, gettext("You do not have permission to delete this user"))}
{:error, error} ->
- {:noreply,
- socket
- |> put_flash(:error, format_ash_error(error))
- |> assign(:show_delete_modal, false)}
+ {:noreply, put_flash(socket, :error, format_ash_error(error))}
end
end
- defp close_delete_modal_and_restore_focus(socket) do
- socket
- |> assign(:show_delete_modal, false)
- |> push_event("focus_restore", %{id: "delete-user-form-trigger"})
- end
-
defp handle_member_linking(socket, user, actor) do
result = perform_member_link_action(socket, user, actor)
diff --git a/lib/mv_web/live/user_live/index.html.heex b/lib/mv_web/live/user_live/index.html.heex
index c84f258..86f0ab7 100644
--- a/lib/mv_web/live/user_live/index.html.heex
+++ b/lib/mv_web/live/user_live/index.html.heex
@@ -1,7 +1,6 @@
<.header>
- {gettext("Users")}
- <:subtitle>{gettext("Manage users and their permissions.")}
+ {gettext("Listing Users")}
<:actions>
<%= if can?(@current_user, :create, Mv.Accounts.User) do %>
<.button variant="primary" navigate={~p"/users/new"} data-testid="user-new">
@@ -38,25 +37,25 @@
{user.role.name}
<:col :let={user} label={gettext("Linked Member")}>
- <.maybe_value value={user.member} empty_sr_text={gettext("No member linked")}>
+ <%= if user.member do %>
{MvWeb.Helpers.MemberHelpers.display_name(user.member)}
-
+ <% else %>
+ {gettext("No member linked")}
+ <% end %>
<:col :let={user} label={gettext("Password")}>
- <.maybe_value
- value={MvWeb.Helpers.UserHelpers.has_password?(user)}
- empty_sr_text={gettext("Not set")}
- >
+ <%= if MvWeb.Helpers.UserHelpers.has_password?(user) do %>
{gettext("Enabled")}
-
+ <% else %>
+ —
+ <% end %>
<:col :let={user} label={gettext("OIDC")}>
- <.maybe_value
- value={MvWeb.Helpers.UserHelpers.has_oidc?(user)}
- empty_sr_text={gettext("Not set")}
- >
+ <%= if MvWeb.Helpers.UserHelpers.has_oidc?(user) do %>
{gettext("Linked")}
-
+ <% else %>
+ —
+ <% end %>
diff --git a/lib/mv_web/live/user_live/show.ex b/lib/mv_web/live/user_live/show.ex
index 770be82..d7a12b2 100644
--- a/lib/mv_web/live/user_live/show.ex
+++ b/lib/mv_web/live/user_live/show.ex
@@ -45,6 +45,8 @@ defmodule MvWeb.UserLive.Show do
{gettext("User")} {@user.email}
+ <:subtitle>{gettext("This is a user record from your database.")}
+
<:actions>
<%= if can?(@current_user, :update, @user) do %>
<.button
@@ -58,106 +60,65 @@ defmodule MvWeb.UserLive.Show do
-
- <.list>
- <:item title={gettext("Email")}>{@user.email}
- <:item title={gettext("Role")}>{@user.role.name}
- <:item title={gettext("Password Authentication")}>
- {if MvWeb.Helpers.UserHelpers.has_password?(@user),
- do: gettext("Enabled"),
- else: gettext("Not enabled")}
-
- <:item title={gettext("OIDC")}>
- {if MvWeb.Helpers.UserHelpers.has_oidc?(@user),
- do: gettext("Linked"),
- else: gettext("Not linked")}
-
- <:item title={gettext("Linked Member")}>
- <%= if @user.member do %>
- <.link
- navigate={~p"/members/#{@user.member}"}
- class="text-blue-600 underline hover:text-blue-800"
- >
- <.icon name="hero-users" class="inline w-4 h-4 mr-1" />
- {MvWeb.Helpers.MemberHelpers.display_name(@user.member)}
-
- <% else %>
-
{gettext("No member linked")}
- <% end %>
-
-
+ <.list>
+ <:item title={gettext("Email")}>{@user.email}
+ <:item title={gettext("Role")}>{@user.role.name}
+ <:item title={gettext("Password Authentication")}>
+ {if MvWeb.Helpers.UserHelpers.has_password?(@user),
+ do: gettext("Enabled"),
+ else: gettext("Not enabled")}
+
+ <:item title={gettext("OIDC")}>
+ {if MvWeb.Helpers.UserHelpers.has_oidc?(@user),
+ do: gettext("Linked"),
+ else: gettext("Not linked")}
+
+ <:item title={gettext("Linked Member")}>
+ <%= if @user.member do %>
+ <.link
+ navigate={~p"/members/#{@user.member}"}
+ class="text-blue-600 underline hover:text-blue-800"
+ >
+ <.icon name="hero-users" class="inline w-4 h-4 mr-1" />
+ {MvWeb.Helpers.MemberHelpers.display_name(@user.member)}
+
+ <% else %>
+
{gettext("No member linked")}
+ <% end %>
+
+
- <%!-- Danger zone: canonical pattern (same as member show) --%>
- <%= if can?(@current_user, :destroy, @user) and not Mv.Helpers.SystemActor.system_user?(@user) do %>
-
-
- {gettext("Danger zone")}
-
-
-
- {gettext(
- "Deleting this user cannot be undone. The user account and any linked member association will be affected."
- )}
-
- <.button
- id="delete-user-trigger"
- variant="danger"
- phx-click="open_delete_modal"
- data-testid="user-delete"
- aria-label={gettext("Delete user %{email}", email: @user.email)}
- >
- <.icon name="hero-trash" class="size-4" />
- {gettext("Delete user")}
-
-
-
- <% end %>
-
- <%!-- Delete User Confirmation Modal (WCAG: focus in modal, keyboard confirm/cancel) --%>
- <%= if assigns[:show_delete_modal] do %>
-
-
-
{gettext("Delete User")}
-
- {gettext(
+ <%!-- Danger zone: canonical pattern (same as member show) --%>
+ <%= if can?(@current_user, :destroy, @user) and not Mv.Helpers.SystemActor.system_user?(@user) do %>
+
+
+ {gettext("Danger zone")}
+
+
+
+ {gettext(
+ "Deleting this user cannot be undone. The user account and any linked member association will be affected."
+ )}
+
+ <.button
+ variant="danger"
+ phx-click="delete"
+ phx-value-id={@user.id}
+ data-confirm={
+ gettext(
"Are you sure you want to delete the user %{email}? This action cannot be undone.",
email: @user.email
- )}
-
-
- <.button
- type="button"
- variant="neutral"
- phx-click="cancel_delete_modal"
- phx-mounted={JS.focus()}
- id="delete-user-modal-cancel"
- aria-label={gettext("Cancel")}
- >
- {gettext("Cancel")}
-
- <.button
- type="button"
- variant="danger"
- phx-click={JS.push("delete", value: %{id: @user.id})}
- aria-label={gettext("Delete user")}
- >
- {gettext("Delete")}
-
-
-
-
- <% end %>
-
+ )
+ }
+ data-testid="user-delete"
+ aria-label={gettext("Delete user %{email}", email: @user.email)}
+ >
+ <.icon name="hero-trash" class="size-4" />
+ {gettext("Delete user")}
+
+
+
+ <% end %>
"""
end
@@ -178,37 +139,10 @@ defmodule MvWeb.UserLive.Show do
{:ok,
socket
|> assign(:page_title, gettext("Show User"))
- |> assign(:user, user)
- |> assign(:show_delete_modal, false)}
+ |> assign(:user, user)}
end
end
- @impl true
- def handle_event("open_delete_modal", _params, socket) do
- {:noreply, assign(socket, :show_delete_modal, true)}
- end
-
- @impl true
- def handle_event("cancel_delete_modal", _params, socket) do
- {:noreply, close_delete_modal_and_restore_focus(socket)}
- end
-
- def handle_event("window_keydown", %{"key" => key}, socket) when key in ["Escape", "Esc"] do
- if socket.assigns[:show_delete_modal] do
- {:noreply, close_delete_modal_and_restore_focus(socket)}
- else
- {:noreply, socket}
- end
- end
-
- def handle_event("window_keydown", _params, socket), do: {:noreply, socket}
-
- def handle_event("dialog_keydown", %{"key" => key}, socket) when key in ["Escape", "Esc"] do
- {:noreply, close_delete_modal_and_restore_focus(socket)}
- end
-
- def handle_event("dialog_keydown", _params, socket), do: {:noreply, socket}
-
@impl true
def handle_event("delete", %{"id" => id}, socket) do
user = socket.assigns.user
@@ -216,16 +150,10 @@ defmodule MvWeb.UserLive.Show do
cond do
to_string(id) != to_string(user.id) ->
- {:noreply,
- socket
- |> put_flash(:error, gettext("User not found"))
- |> assign(:show_delete_modal, false)}
+ {:noreply, put_flash(socket, :error, gettext("User not found"))}
Mv.Helpers.SystemActor.system_user?(user) ->
- {:noreply,
- socket
- |> put_flash(:error, gettext("System user cannot be deleted."))
- |> assign(:show_delete_modal, false)}
+ {:noreply, put_flash(socket, :error, gettext("System user cannot be deleted."))}
true ->
handle_user_delete_destroy(socket, user, actor)
@@ -242,21 +170,10 @@ defmodule MvWeb.UserLive.Show do
{:error, %Ash.Error.Forbidden{}} ->
{:noreply,
- socket
- |> put_flash(:error, gettext("You do not have permission to delete this user"))
- |> assign(:show_delete_modal, false)}
+ put_flash(socket, :error, gettext("You do not have permission to delete this user"))}
{:error, error} ->
- {:noreply,
- socket
- |> put_flash(:error, format_ash_error(error))
- |> assign(:show_delete_modal, false)}
+ {:noreply, put_flash(socket, :error, format_ash_error(error))}
end
end
-
- defp close_delete_modal_and_restore_focus(socket) do
- socket
- |> assign(:show_delete_modal, false)
- |> push_event("focus_restore", %{id: "delete-user-trigger"})
- end
end
diff --git a/lib/mv_web/member_live/index/membership_fee_status.ex b/lib/mv_web/member_live/index/membership_fee_status.ex
index d586b28..bf4dd53 100644
--- a/lib/mv_web/member_live/index/membership_fee_status.ex
+++ b/lib/mv_web/member_live/index/membership_fee_status.ex
@@ -93,30 +93,22 @@ defmodule MvWeb.MemberLive.Index.MembershipFeeStatus do
## Returns
- Map with `:variant`, `:icon`, and `:label` keys (and legacy `:color`), or `nil` if status is nil.
- Use `:variant` with <.badge variant={badge.variant}> for WCAG-compliant rendering.
+ Map with `:color`, `:icon`, and `:label` keys, or `nil` if status is nil
## Examples
iex> MvWeb.MemberLive.Index.MembershipFeeStatus.format_cycle_status_badge(:paid)
- %{variant: :success, color: "badge-success", icon: "hero-check-circle", label: "Paid"}
+ %{color: "badge-success", icon: "hero-check-circle", label: "Paid"}
iex> MvWeb.MemberLive.Index.MembershipFeeStatus.format_cycle_status_badge(nil)
nil
"""
@spec format_cycle_status_badge(:paid | :unpaid | :suspended | nil) ::
- %{
- variant: :success | :error | :warning,
- color: String.t(),
- icon: String.t(),
- label: String.t()
- }
- | nil
+ %{color: String.t(), icon: String.t(), label: String.t()} | nil
def format_cycle_status_badge(nil), do: nil
def format_cycle_status_badge(status) when status in [:paid, :unpaid, :suspended] do
%{
- variant: MembershipFeeHelpers.status_variant(status),
color: MembershipFeeHelpers.status_color(status),
icon: MembershipFeeHelpers.status_icon(status),
label: format_status_label(status)
diff --git a/priv/gettext/de/LC_MESSAGES/default.po b/priv/gettext/de/LC_MESSAGES/default.po
index 32409ea..2f3a1f8 100644
--- a/priv/gettext/de/LC_MESSAGES/default.po
+++ b/priv/gettext/de/LC_MESSAGES/default.po
@@ -36,12 +36,7 @@ msgid "City"
msgstr "Stadt"
#: lib/mv_web/live/group_live/show.ex
-#: lib/mv_web/live/member_live/form.ex
-#: lib/mv_web/live/member_live/show.ex
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
-#: lib/mv_web/live/role_live/show.ex
-#: lib/mv_web/live/user_live/form.ex
-#: lib/mv_web/live/user_live/show.ex
#, elixir-autogen, elixir-format
msgid "Delete"
msgstr "Löschen"
@@ -262,12 +257,9 @@ msgstr "Dein Passwort wurde erfolgreich zurückgesetzt"
#: lib/mv_web/live/group_live/show.ex
#: lib/mv_web/live/member_field_live/form_component.ex
#: lib/mv_web/live/member_live/form.ex
-#: lib/mv_web/live/member_live/show.ex
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
#: lib/mv_web/live/membership_fee_type_live/form.ex
-#: lib/mv_web/live/role_live/show.ex
#: lib/mv_web/live/user_live/form.ex
-#: lib/mv_web/live/user_live/show.ex
#, elixir-autogen, elixir-format
msgid "Cancel"
msgstr "Abbrechen"
@@ -302,6 +294,7 @@ msgid "Logout"
msgstr "Abmelden"
#: lib/mv_web/live/user_live/index.ex
+#: lib/mv_web/live/user_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Listing Users"
msgstr "Benutzer*innen auflisten"
@@ -388,6 +381,16 @@ msgstr "Benutzer*in speichern"
msgid "Show User"
msgstr "Benutzer*in anzeigen"
+#: lib/mv_web/live/user_live/show.ex
+#, elixir-autogen, elixir-format
+msgid "This is a user record from your database."
+msgstr "Dies ist ein Benutzer*innen-Datensatz aus Ihrer Datenbank."
+
+#: lib/mv_web/live/user_live/form.ex
+#, elixir-autogen, elixir-format
+msgid "Use this form to manage user records in your database."
+msgstr "Verwende dieses Formular, um Benutzer*innen-Datensätze zu verwalten."
+
#: lib/mv_web/live/user_live/form.ex
#: lib/mv_web/live/user_live/show.ex
#, elixir-autogen, elixir-format
@@ -530,7 +533,6 @@ msgstr "Suchen..."
#: lib/mv_web/components/layouts/sidebar.ex
#: lib/mv_web/live/role_live/index.html.heex
-#: lib/mv_web/live/user_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Users"
msgstr "Benutzer*innen"
@@ -591,6 +593,18 @@ msgstr "Diese E-Mail-Adresse ist bereits mit einem anderen OIDC-Konto verknüpft
msgid "Custom Fields"
msgstr "Benutzerdefinierte Felder"
+#: lib/mv_web/live/custom_field_live/index_component.ex
+#, elixir-autogen, elixir-format
+msgid "%{count} member has a value assigned for this custom field."
+msgid_plural "%{count} members have values assigned for this custom field."
+msgstr[0] "%{count} Mitglied hat einen Wert für dieses benutzerdefinierte Feld zugewiesen."
+msgstr[1] "%{count} Mitglieder haben Werte für dieses benutzerdefinierte Feld zugewiesen."
+
+#: lib/mv_web/live/custom_field_live/index_component.ex
+#, elixir-autogen, elixir-format
+msgid "All custom field values will be permanently deleted when you delete this custom field."
+msgstr "Alle benutzerdefinierten Feldwerte werden beim Löschen dieses benutzerdefinierten Feldes dauerhaft gelöscht."
+
#: lib/mv_web/live/custom_field_live/index_component.ex
#, elixir-autogen, elixir-format
msgid "Enter the text above to confirm"
@@ -776,7 +790,6 @@ msgstr "Beitragsdaten"
msgid "Payments"
msgstr "Zahlungen"
-#: lib/mv_web/live/datafields_live.ex
#: lib/mv_web/live/member_live/form.ex
#: lib/mv_web/live/member_live/show.ex
#, elixir-autogen, elixir-format
@@ -1375,8 +1388,6 @@ msgid "None (no default)"
msgstr "Keine (kein Standard)"
#: lib/mv_web/live/member_live/show.ex
-#: lib/mv_web/live/member_live/show/membership_fees_component.ex
-#: lib/mv_web/live/user_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Not set"
msgstr "Nicht gesetzt"
@@ -1462,6 +1473,11 @@ msgstr "Art"
msgid "Type '%{confirmation}' to confirm"
msgstr "Gib '%{confirmation}' ein, um zu bestätigen"
+#: lib/mv_web/live/membership_fee_type_live/form.ex
+#, elixir-autogen, elixir-format
+msgid "Use this form to manage membership fee types in your database."
+msgstr "Verwende dieses Formular, um Mitgliedsbeitragsarten in deiner Datenbank zu verwalten."
+
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
#, elixir-autogen, elixir-format
msgid "Warning"
@@ -1688,6 +1704,11 @@ msgstr "System-Rollen können nicht gelöscht werden."
msgid "Toggle sidebar"
msgstr "Sidebar umschalten"
+#: lib/mv_web/live/role_live/form.ex
+#, elixir-autogen, elixir-format
+msgid "Use this form to manage roles in your database."
+msgstr "Verwende dieses Formular, um Rollen in deiner Datenbank zu verwalten."
+
#: lib/mv_web/components/layouts/sidebar.ex
#, elixir-autogen, elixir-format
msgid "User menu"
@@ -2407,6 +2428,11 @@ msgstr "Alle Jahre zusammengefasst (Kreis)"
msgid "Contributions by year"
msgstr "Beiträge nach Jahr"
+#: lib/mv_web/live/statistics_live.ex
+#, elixir-autogen, elixir-format
+msgid "Overview from first membership to today"
+msgstr "Übersicht vom ersten Eintritt bis heute"
+
#: lib/mv_web/live/statistics_live.ex
#, elixir-autogen, elixir-format
msgid "Contributions by year as table with stacked bars"
@@ -2884,6 +2910,11 @@ msgstr "CSV Datei auswählen"
msgid "Import Members"
msgstr "Mitglieder importieren (CSV)"
+#~ #: lib/mv_web/live/import_live.ex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "Datei auswählen"
+#~ msgstr ""
+
#: lib/mv_web/live/global_settings_live.ex
#, elixir-autogen, elixir-format
msgid "Admin group name"
@@ -2909,6 +2940,21 @@ msgstr "Client-ID"
msgid "Client Secret"
msgstr "Client-Geheimnis"
+#: lib/mv_web/live/membership_fee_settings_live.ex
+#, elixir-autogen, elixir-format, fuzzy
+msgid "Configure global settings and fee types for membership fees."
+msgstr "Globale Einstellungen für Mitgliedsbeiträge konfigurieren."
+
+#: lib/mv_web/live/datafields_live.ex
+#, elixir-autogen, elixir-format
+msgid "Configure member fields and custom data fields."
+msgstr "Mitgliedsfelder und benutzerdefinierte Datenfelder konfigurieren."
+
+#: lib/mv_web/live/datafields_live.ex
+#, elixir-autogen, elixir-format, fuzzy
+msgid "Custom fields"
+msgstr "Benutzerdefinierte Felder"
+
#: lib/mv_web/components/layouts/sidebar.ex
#: lib/mv_web/live/datafields_live.ex
#, elixir-autogen, elixir-format
@@ -2950,6 +2996,11 @@ msgstr "Aus OIDC_REDIRECT_URI"
msgid "Groups claim"
msgstr "Gruppenclaim"
+#: lib/mv_web/live/datafields_live.ex
+#, elixir-autogen, elixir-format
+msgid "Member fields"
+msgstr "Mitgliedsfelder"
+
#: lib/mv_web/components/layouts/sidebar.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Membership fee settings"
@@ -3174,64 +3225,88 @@ msgstr "Das Löschen dieses Datenfeldes kann nicht rückgängig gemacht werden.
msgid "Individual datafields"
msgstr "Individuelle Datenfelder"
-#: lib/mv_web/live/member_live/form.ex
-#: lib/mv_web/live/member_live/show.ex
-#, elixir-autogen, elixir-format, fuzzy
-msgid "Delete Member"
-msgstr "Mitglied löschen"
+#~ #: lib/mv_web/live/member_field_live/form_component.ex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "Back to Settings"
+#~ msgstr "Zurück zu den Einstellungen"
-#: lib/mv_web/live/role_live/show.ex
-#, elixir-autogen, elixir-format, fuzzy
-msgid "Delete Role"
-msgstr "Rolle löschen"
+#~ #: lib/mv_web/live/role_live/index.html.heex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "Cannot delete system role"
+#~ msgstr "System-Rolle kann nicht gelöscht werden"
-#: lib/mv_web/live/user_live/form.ex
-#: lib/mv_web/live/user_live/show.ex
-#, elixir-autogen, elixir-format, fuzzy
-msgid "Delete User"
-msgstr "Benutzer*in löschen"
+#~ #: lib/mv_web/live/custom_field_live/index_component.ex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "Click for custom field details"
+#~ msgstr "Klicke für Datenfeld-Details"
-#: lib/mv_web/live/membership_fee_settings_live.ex
-#, elixir-autogen, elixir-format
-msgid "Configure fee types for membership fees."
-msgstr "Verwalte Beitragsarten und Mitgliedsbeiträge."
+#~ #: lib/mv_web/live/member_field_live/index_component.ex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "Click for datafield details"
+#~ msgstr "Klicke für Datenfeld-Details"
-#: lib/mv_web/live/datafields_live.ex
-#, elixir-autogen, elixir-format
-msgid "Configure which data you want to save for your members. Define individual datafields."
-msgstr "Verwalte welche Daten du für eure Mitglieder speichern möchtest. Lege individuelle datenfelder an."
+#~ #: lib/mv_web/live/member_live/form.ex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "Coming soon"
+#~ msgstr "Demnächst verfügbar"
-#: lib/mv_web/live/user_live/index.html.heex
-#, elixir-autogen, elixir-format, fuzzy
-msgid "Manage users and their permissions."
-msgstr "Verwalte Benutzer*innen und ihre Berechtigungen."
+#~ #: lib/mv_web/live/components/field_visibility_dropdown_component.ex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "Custom Field %{id}"
+#~ msgstr "Benutzerdefiniertes Feld %{id}"
-#: lib/mv_web/live/custom_field_live/index_component.ex
-#, elixir-autogen, elixir-format, fuzzy
-msgid "%{count} member has a value assigned for this datafield."
-msgid_plural "%{count} members have values assigned for this datafield."
-msgstr[0] "%{count} Mitglied hat einen Wert für dieses benutzerdefinierte Feld zugewiesen."
-msgstr[1] "%{count} Mitglieder haben Werte für dieses benutzerdefinierte Feld zugewiesen."
+#~ #: lib/mv_web/live/custom_field_live/index_component.ex
+#~ #: lib/mv_web/live/member_field_live/index_component.ex
+#~ #, elixir-autogen, elixir-format, fuzzy
+#~ msgid "Edit datafield"
+#~ msgstr "Datenfeld bearbeiten"
-#: lib/mv_web/live/datafields_live.ex
-#, elixir-autogen, elixir-format, fuzzy
-msgid "Individual Datafields"
-msgstr "Individuelle Datenfelder"
+#~ #: lib/mv_web/live/user_live/index.html.heex
+#~ #, elixir-autogen, elixir-format, fuzzy
+#~ msgid "Edit user"
+#~ msgstr "Benutzer*in bearbeiten"
-#: lib/mv_web/live/member_live/index.html.heex
-#, elixir-autogen, elixir-format, fuzzy
-msgid "No group assignment"
-msgstr "Keine Gruppenzuordnung"
+#~ #: lib/mv_web/live/components/member_filter_component.ex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "Reset"
+#~ msgstr "Zurücksetzen"
-#: lib/mv_web/components/core_components.ex
-#: lib/mv_web/live/group_live/index.ex
-#: lib/mv_web/live/member_live/index.html.heex
-#: lib/mv_web/live/member_live/show/membership_fees_component.ex
-#, elixir-autogen, elixir-format
-msgid "Not specified"
-msgstr "Nicht angegeben"
+#~ #: lib/mv_web/live/role_live/show.ex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "Rolle bearbeiten"
+#~ msgstr "Rolle bearbeiten"
-#: lib/mv_web/live/custom_field_live/index_component.ex
-#, elixir-autogen, elixir-format, fuzzy
-msgid "All datafield values will be permanently deleted when you delete this datafield."
-msgstr "Alle benutzerdefinierten Feldwerte werden beim Löschen dieses benutzerdefinierten Feldes dauerhaft gelöscht."
+#~ #: lib/mv_web/live/role_live/form.ex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "Save Role"
+#~ msgstr "Rolle speichern"
+
+#~ #: lib/mv_web/live/user_live/index.html.heex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "Select all users"
+#~ msgstr "Alle Benutzer*innen auswählen"
+
+#~ #: lib/mv_web/live/user_live/index.html.heex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "Select user"
+#~ msgstr "Benutzer*in auswählen"
+
+#~ #: lib/mv_web/live/role_live/index.html.heex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "System roles cannot be deleted"
+#~ msgstr "System-Rollen können nicht gelöscht werden"
+
+#~ #: lib/mv_web/live/group_live/index.ex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "View"
+#~ msgstr "Anzeigen"
+
+#~ #: lib/mv_web/live/member_live/index.ex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "You do not have permission to access this member"
+#~ msgstr "Du hast keine Berechtigung, auf dieses Mitglied zuzugreifen"
+
+#~ #: lib/mv_web/live/user_live/index.ex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "You do not have permission to access this user"
+#~ msgstr "Du hast keine Berechtigung, auf diese*n Benutzer*in zuzugreifen"
diff --git a/priv/gettext/default.pot b/priv/gettext/default.pot
index 6b80e27..7413a20 100644
--- a/priv/gettext/default.pot
+++ b/priv/gettext/default.pot
@@ -37,12 +37,7 @@ msgid "City"
msgstr ""
#: lib/mv_web/live/group_live/show.ex
-#: lib/mv_web/live/member_live/form.ex
-#: lib/mv_web/live/member_live/show.ex
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
-#: lib/mv_web/live/role_live/show.ex
-#: lib/mv_web/live/user_live/form.ex
-#: lib/mv_web/live/user_live/show.ex
#, elixir-autogen, elixir-format
msgid "Delete"
msgstr ""
@@ -263,12 +258,9 @@ msgstr ""
#: lib/mv_web/live/group_live/show.ex
#: lib/mv_web/live/member_field_live/form_component.ex
#: lib/mv_web/live/member_live/form.ex
-#: lib/mv_web/live/member_live/show.ex
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
#: lib/mv_web/live/membership_fee_type_live/form.ex
-#: lib/mv_web/live/role_live/show.ex
#: lib/mv_web/live/user_live/form.ex
-#: lib/mv_web/live/user_live/show.ex
#, elixir-autogen, elixir-format
msgid "Cancel"
msgstr ""
@@ -303,6 +295,7 @@ msgid "Logout"
msgstr ""
#: lib/mv_web/live/user_live/index.ex
+#: lib/mv_web/live/user_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Listing Users"
msgstr ""
@@ -389,6 +382,16 @@ msgstr ""
msgid "Show User"
msgstr ""
+#: lib/mv_web/live/user_live/show.ex
+#, elixir-autogen, elixir-format
+msgid "This is a user record from your database."
+msgstr ""
+
+#: lib/mv_web/live/user_live/form.ex
+#, elixir-autogen, elixir-format
+msgid "Use this form to manage user records in your database."
+msgstr ""
+
#: lib/mv_web/live/user_live/form.ex
#: lib/mv_web/live/user_live/show.ex
#, elixir-autogen, elixir-format
@@ -531,7 +534,6 @@ msgstr ""
#: lib/mv_web/components/layouts/sidebar.ex
#: lib/mv_web/live/role_live/index.html.heex
-#: lib/mv_web/live/user_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Users"
msgstr ""
@@ -592,6 +594,18 @@ msgstr ""
msgid "Custom Fields"
msgstr ""
+#: lib/mv_web/live/custom_field_live/index_component.ex
+#, elixir-autogen, elixir-format
+msgid "%{count} member has a value assigned for this custom field."
+msgid_plural "%{count} members have values assigned for this custom field."
+msgstr[0] ""
+msgstr[1] ""
+
+#: lib/mv_web/live/custom_field_live/index_component.ex
+#, elixir-autogen, elixir-format
+msgid "All custom field values will be permanently deleted when you delete this custom field."
+msgstr ""
+
#: lib/mv_web/live/custom_field_live/index_component.ex
#, elixir-autogen, elixir-format
msgid "Enter the text above to confirm"
@@ -777,7 +791,6 @@ msgstr ""
msgid "Payments"
msgstr ""
-#: lib/mv_web/live/datafields_live.ex
#: lib/mv_web/live/member_live/form.ex
#: lib/mv_web/live/member_live/show.ex
#, elixir-autogen, elixir-format
@@ -1376,8 +1389,6 @@ msgid "None (no default)"
msgstr ""
#: lib/mv_web/live/member_live/show.ex
-#: lib/mv_web/live/member_live/show/membership_fees_component.ex
-#: lib/mv_web/live/user_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Not set"
msgstr ""
@@ -1463,6 +1474,11 @@ msgstr ""
msgid "Type '%{confirmation}' to confirm"
msgstr ""
+#: lib/mv_web/live/membership_fee_type_live/form.ex
+#, elixir-autogen, elixir-format
+msgid "Use this form to manage membership fee types in your database."
+msgstr ""
+
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
#, elixir-autogen, elixir-format
msgid "Warning"
@@ -1689,6 +1705,11 @@ msgstr ""
msgid "Toggle sidebar"
msgstr ""
+#: lib/mv_web/live/role_live/form.ex
+#, elixir-autogen, elixir-format
+msgid "Use this form to manage roles in your database."
+msgstr ""
+
#: lib/mv_web/components/layouts/sidebar.ex
#, elixir-autogen, elixir-format
msgid "User menu"
@@ -2408,6 +2429,11 @@ msgstr ""
msgid "Contributions by year"
msgstr ""
+#: lib/mv_web/live/statistics_live.ex
+#, elixir-autogen, elixir-format
+msgid "Overview from first membership to today"
+msgstr ""
+
#: lib/mv_web/live/statistics_live.ex
#, elixir-autogen, elixir-format
msgid "Contributions by year as table with stacked bars"
@@ -2909,6 +2935,21 @@ msgstr ""
msgid "Client Secret"
msgstr ""
+#: lib/mv_web/live/membership_fee_settings_live.ex
+#, elixir-autogen, elixir-format
+msgid "Configure global settings and fee types for membership fees."
+msgstr ""
+
+#: lib/mv_web/live/datafields_live.ex
+#, elixir-autogen, elixir-format
+msgid "Configure member fields and custom data fields."
+msgstr ""
+
+#: lib/mv_web/live/datafields_live.ex
+#, elixir-autogen, elixir-format
+msgid "Custom fields"
+msgstr ""
+
#: lib/mv_web/components/layouts/sidebar.ex
#: lib/mv_web/live/datafields_live.ex
#, elixir-autogen, elixir-format
@@ -2950,6 +2991,11 @@ msgstr ""
msgid "Groups claim"
msgstr ""
+#: lib/mv_web/live/datafields_live.ex
+#, elixir-autogen, elixir-format
+msgid "Member fields"
+msgstr ""
+
#: lib/mv_web/components/layouts/sidebar.ex
#, elixir-autogen, elixir-format
msgid "Membership fee settings"
@@ -3173,65 +3219,3 @@ msgstr ""
#, elixir-autogen, elixir-format
msgid "Individual datafields"
msgstr ""
-
-#: lib/mv_web/live/member_live/form.ex
-#: lib/mv_web/live/member_live/show.ex
-#, elixir-autogen, elixir-format
-msgid "Delete Member"
-msgstr ""
-
-#: lib/mv_web/live/role_live/show.ex
-#, elixir-autogen, elixir-format
-msgid "Delete Role"
-msgstr ""
-
-#: lib/mv_web/live/user_live/form.ex
-#: lib/mv_web/live/user_live/show.ex
-#, elixir-autogen, elixir-format
-msgid "Delete User"
-msgstr ""
-
-#: lib/mv_web/live/membership_fee_settings_live.ex
-#, elixir-autogen, elixir-format
-msgid "Configure fee types for membership fees."
-msgstr ""
-
-#: lib/mv_web/live/datafields_live.ex
-#, elixir-autogen, elixir-format
-msgid "Configure which data you want to save for your members. Define individual datafields."
-msgstr ""
-
-#: lib/mv_web/live/user_live/index.html.heex
-#, elixir-autogen, elixir-format
-msgid "Manage users and their permissions."
-msgstr ""
-
-#: lib/mv_web/live/custom_field_live/index_component.ex
-#, elixir-autogen, elixir-format
-msgid "%{count} member has a value assigned for this datafield."
-msgid_plural "%{count} members have values assigned for this datafield."
-msgstr[0] ""
-msgstr[1] ""
-
-#: lib/mv_web/live/datafields_live.ex
-#, elixir-autogen, elixir-format
-msgid "Individual Datafields"
-msgstr ""
-
-#: lib/mv_web/live/member_live/index.html.heex
-#, elixir-autogen, elixir-format
-msgid "No group assignment"
-msgstr ""
-
-#: lib/mv_web/components/core_components.ex
-#: lib/mv_web/live/group_live/index.ex
-#: lib/mv_web/live/member_live/index.html.heex
-#: lib/mv_web/live/member_live/show/membership_fees_component.ex
-#, elixir-autogen, elixir-format
-msgid "Not specified"
-msgstr ""
-
-#: lib/mv_web/live/custom_field_live/index_component.ex
-#, elixir-autogen, elixir-format
-msgid "All datafield values will be permanently deleted when you delete this datafield."
-msgstr ""
diff --git a/priv/gettext/en/LC_MESSAGES/default.po b/priv/gettext/en/LC_MESSAGES/default.po
index 2ea6be5..51aab4f 100644
--- a/priv/gettext/en/LC_MESSAGES/default.po
+++ b/priv/gettext/en/LC_MESSAGES/default.po
@@ -37,12 +37,7 @@ msgid "City"
msgstr ""
#: lib/mv_web/live/group_live/show.ex
-#: lib/mv_web/live/member_live/form.ex
-#: lib/mv_web/live/member_live/show.ex
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
-#: lib/mv_web/live/role_live/show.ex
-#: lib/mv_web/live/user_live/form.ex
-#: lib/mv_web/live/user_live/show.ex
#, elixir-autogen, elixir-format
msgid "Delete"
msgstr ""
@@ -263,12 +258,9 @@ msgstr ""
#: lib/mv_web/live/group_live/show.ex
#: lib/mv_web/live/member_field_live/form_component.ex
#: lib/mv_web/live/member_live/form.ex
-#: lib/mv_web/live/member_live/show.ex
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
#: lib/mv_web/live/membership_fee_type_live/form.ex
-#: lib/mv_web/live/role_live/show.ex
#: lib/mv_web/live/user_live/form.ex
-#: lib/mv_web/live/user_live/show.ex
#, elixir-autogen, elixir-format
msgid "Cancel"
msgstr ""
@@ -303,6 +295,7 @@ msgid "Logout"
msgstr ""
#: lib/mv_web/live/user_live/index.ex
+#: lib/mv_web/live/user_live/index.html.heex
#, elixir-autogen, elixir-format, fuzzy
msgid "Listing Users"
msgstr ""
@@ -389,6 +382,16 @@ msgstr ""
msgid "Show User"
msgstr ""
+#: lib/mv_web/live/user_live/show.ex
+#, elixir-autogen, elixir-format, fuzzy
+msgid "This is a user record from your database."
+msgstr ""
+
+#: lib/mv_web/live/user_live/form.ex
+#, elixir-autogen, elixir-format, fuzzy
+msgid "Use this form to manage user records in your database."
+msgstr ""
+
#: lib/mv_web/live/user_live/form.ex
#: lib/mv_web/live/user_live/show.ex
#, elixir-autogen, elixir-format
@@ -531,7 +534,6 @@ msgstr ""
#: lib/mv_web/components/layouts/sidebar.ex
#: lib/mv_web/live/role_live/index.html.heex
-#: lib/mv_web/live/user_live/index.html.heex
#, elixir-autogen, elixir-format, fuzzy
msgid "Users"
msgstr ""
@@ -592,6 +594,18 @@ msgstr ""
msgid "Custom Fields"
msgstr ""
+#: lib/mv_web/live/custom_field_live/index_component.ex
+#, elixir-autogen, elixir-format
+msgid "%{count} member has a value assigned for this custom field."
+msgid_plural "%{count} members have values assigned for this custom field."
+msgstr[0] ""
+msgstr[1] ""
+
+#: lib/mv_web/live/custom_field_live/index_component.ex
+#, elixir-autogen, elixir-format
+msgid "All custom field values will be permanently deleted when you delete this custom field."
+msgstr ""
+
#: lib/mv_web/live/custom_field_live/index_component.ex
#, elixir-autogen, elixir-format
msgid "Enter the text above to confirm"
@@ -777,7 +791,6 @@ msgstr ""
msgid "Payments"
msgstr ""
-#: lib/mv_web/live/datafields_live.ex
#: lib/mv_web/live/member_live/form.ex
#: lib/mv_web/live/member_live/show.ex
#, elixir-autogen, elixir-format
@@ -1376,8 +1389,6 @@ msgid "None (no default)"
msgstr ""
#: lib/mv_web/live/member_live/show.ex
-#: lib/mv_web/live/member_live/show/membership_fees_component.ex
-#: lib/mv_web/live/user_live/index.html.heex
#, elixir-autogen, elixir-format, fuzzy
msgid "Not set"
msgstr ""
@@ -1463,6 +1474,11 @@ msgstr ""
msgid "Type '%{confirmation}' to confirm"
msgstr ""
+#: lib/mv_web/live/membership_fee_type_live/form.ex
+#, elixir-autogen, elixir-format, fuzzy
+msgid "Use this form to manage membership fee types in your database."
+msgstr ""
+
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
#, elixir-autogen, elixir-format
msgid "Warning"
@@ -1689,6 +1705,11 @@ msgstr ""
msgid "Toggle sidebar"
msgstr ""
+#: lib/mv_web/live/role_live/form.ex
+#, elixir-autogen, elixir-format, fuzzy
+msgid "Use this form to manage roles in your database."
+msgstr ""
+
#: lib/mv_web/components/layouts/sidebar.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "User menu"
@@ -2408,6 +2429,11 @@ msgstr ""
msgid "Contributions by year"
msgstr ""
+#: lib/mv_web/live/statistics_live.ex
+#, elixir-autogen, elixir-format
+msgid "Overview from first membership to today"
+msgstr ""
+
#: lib/mv_web/live/statistics_live.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Contributions by year as table with stacked bars"
@@ -2909,6 +2935,21 @@ msgstr ""
msgid "Client Secret"
msgstr ""
+#: lib/mv_web/live/membership_fee_settings_live.ex
+#, elixir-autogen, elixir-format, fuzzy
+msgid "Configure global settings and fee types for membership fees."
+msgstr ""
+
+#: lib/mv_web/live/datafields_live.ex
+#, elixir-autogen, elixir-format
+msgid "Configure member fields and custom data fields."
+msgstr ""
+
+#: lib/mv_web/live/datafields_live.ex
+#, elixir-autogen, elixir-format, fuzzy
+msgid "Custom fields"
+msgstr ""
+
#: lib/mv_web/components/layouts/sidebar.ex
#: lib/mv_web/live/datafields_live.ex
#, elixir-autogen, elixir-format
@@ -2950,6 +2991,11 @@ msgstr ""
msgid "Groups claim"
msgstr ""
+#: lib/mv_web/live/datafields_live.ex
+#, elixir-autogen, elixir-format, fuzzy
+msgid "Member fields"
+msgstr ""
+
#: lib/mv_web/components/layouts/sidebar.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Membership fee settings"
@@ -3174,64 +3220,88 @@ msgstr ""
msgid "Individual datafields"
msgstr ""
-#: lib/mv_web/live/member_live/form.ex
-#: lib/mv_web/live/member_live/show.ex
-#, elixir-autogen, elixir-format, fuzzy
-msgid "Delete Member"
-msgstr ""
+#~ #: lib/mv_web/live/member_field_live/form_component.ex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "Back to Settings"
+#~ msgstr ""
-#: lib/mv_web/live/role_live/show.ex
-#, elixir-autogen, elixir-format, fuzzy
-msgid "Delete Role"
-msgstr ""
+#~ #: lib/mv_web/live/role_live/index.html.heex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "Cannot delete system role"
+#~ msgstr ""
-#: lib/mv_web/live/user_live/form.ex
-#: lib/mv_web/live/user_live/show.ex
-#, elixir-autogen, elixir-format, fuzzy
-msgid "Delete User"
-msgstr ""
+#~ #: lib/mv_web/live/custom_field_live/index_component.ex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "Click for custom field details"
+#~ msgstr ""
-#: lib/mv_web/live/membership_fee_settings_live.ex
-#, elixir-autogen, elixir-format
-msgid "Configure fee types for membership fees."
-msgstr ""
+#~ #: lib/mv_web/live/member_field_live/index_component.ex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "Click for datafield details"
+#~ msgstr ""
-#: lib/mv_web/live/datafields_live.ex
-#, elixir-autogen, elixir-format
-msgid "Configure which data you want to save for your members. Define individual datafields."
-msgstr ""
+#~ #: lib/mv_web/live/member_live/form.ex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "Coming soon"
+#~ msgstr ""
-#: lib/mv_web/live/user_live/index.html.heex
-#, elixir-autogen, elixir-format, fuzzy
-msgid "Manage users and their permissions."
-msgstr ""
+#~ #: lib/mv_web/live/components/field_visibility_dropdown_component.ex
+#~ #, elixir-autogen, elixir-format, fuzzy
+#~ msgid "Custom Field %{id}"
+#~ msgstr ""
-#: lib/mv_web/live/custom_field_live/index_component.ex
-#, elixir-autogen, elixir-format, fuzzy
-msgid "%{count} member has a value assigned for this datafield."
-msgid_plural "%{count} members have values assigned for this datafield."
-msgstr[0] ""
-msgstr[1] ""
+#~ #: lib/mv_web/live/custom_field_live/index_component.ex
+#~ #: lib/mv_web/live/member_field_live/index_component.ex
+#~ #, elixir-autogen, elixir-format, fuzzy
+#~ msgid "Edit datafield"
+#~ msgstr ""
-#: lib/mv_web/live/datafields_live.ex
-#, elixir-autogen, elixir-format, fuzzy
-msgid "Individual Datafields"
-msgstr ""
+#~ #: lib/mv_web/live/user_live/index.html.heex
+#~ #, elixir-autogen, elixir-format, fuzzy
+#~ msgid "Edit user"
+#~ msgstr ""
-#: lib/mv_web/live/member_live/index.html.heex
-#, elixir-autogen, elixir-format, fuzzy
-msgid "No group assignment"
-msgstr ""
+#~ #: lib/mv_web/live/components/member_filter_component.ex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "Reset"
+#~ msgstr ""
-#: lib/mv_web/components/core_components.ex
-#: lib/mv_web/live/group_live/index.ex
-#: lib/mv_web/live/member_live/index.html.heex
-#: lib/mv_web/live/member_live/show/membership_fees_component.ex
-#, elixir-autogen, elixir-format
-msgid "Not specified"
-msgstr ""
+#~ #: lib/mv_web/live/role_live/show.ex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "Rolle bearbeiten"
+#~ msgstr ""
-#: lib/mv_web/live/custom_field_live/index_component.ex
-#, elixir-autogen, elixir-format, fuzzy
-msgid "All datafield values will be permanently deleted when you delete this datafield."
-msgstr ""
+#~ #: lib/mv_web/live/role_live/form.ex
+#~ #, elixir-autogen, elixir-format, fuzzy
+#~ msgid "Save Role"
+#~ msgstr ""
+
+#~ #: lib/mv_web/live/user_live/index.html.heex
+#~ #, elixir-autogen, elixir-format, fuzzy
+#~ msgid "Select all users"
+#~ msgstr ""
+
+#~ #: lib/mv_web/live/user_live/index.html.heex
+#~ #, elixir-autogen, elixir-format, fuzzy
+#~ msgid "Select user"
+#~ msgstr ""
+
+#~ #: lib/mv_web/live/role_live/index.html.heex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "System roles cannot be deleted"
+#~ msgstr ""
+
+#~ #: lib/mv_web/live/group_live/index.ex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "View"
+#~ msgstr ""
+
+#~ #: lib/mv_web/live/member_live/index.ex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "You do not have permission to access this member"
+#~ msgstr ""
+
+#~ #: lib/mv_web/live/user_live/index.ex
+#~ #, elixir-autogen, elixir-format, fuzzy
+#~ msgid "You do not have permission to access this user"
+#~ msgstr ""
diff --git a/test/mv_web/components/core_components_badge_test.exs b/test/mv_web/components/core_components_badge_test.exs
deleted file mode 100644
index 717dde6..0000000
--- a/test/mv_web/components/core_components_badge_test.exs
+++ /dev/null
@@ -1,91 +0,0 @@
-defmodule MvWeb.Components.CoreComponentsBadgeTest do
- @moduledoc """
- Unit tests for the Core Components badge (WCAG-compliant, non-transparent).
- """
- use MvWeb.ConnCase, async: true
-
- import Phoenix.Component
- import Phoenix.LiveViewTest
- import MvWeb.CoreComponents
-
- describe "badge/1" do
- test "default variant renders with badge and badge-neutral classes (visible, not ghost)" do
- assigns = %{}
-
- html =
- rendered_to_string(~H"""
- <.badge variant="neutral">Label
- """)
-
- assert html =~ "badge"
- assert html =~ "badge-neutral"
- assert html =~ "badge-soft"
- refute html =~ "badge-ghost"
- assert html =~ "Label"
- end
-
- test "success variant renders badge-success and badge-soft" do
- assigns = %{}
-
- html =
- rendered_to_string(~H"""
- <.badge variant="success">Paid
- """)
-
- assert html =~ "badge-success"
- assert html =~ "badge-soft"
- assert html =~ "Paid"
- end
-
- test "outline style includes bg-base-100 for contrast" do
- assigns = %{}
-
- html =
- rendered_to_string(~H"""
- <.badge variant="primary" style="outline">Outline
- """)
-
- assert html =~ "badge-outline"
- assert html =~ "bg-base-100"
- assert html =~ "Outline"
- end
-
- test "solid style has no badge-soft or badge-outline" do
- assigns = %{}
-
- html =
- rendered_to_string(~H"""
- <.badge variant="error" style="solid">Error
- """)
-
- assert html =~ "badge-error"
- refute html =~ "badge-soft"
- refute html =~ "badge-outline"
- assert html =~ "Error"
- end
-
- test "size sm adds badge-sm" do
- assigns = %{}
-
- html =
- rendered_to_string(~H"""
- <.badge variant="neutral" size="sm">Small
- """)
-
- assert html =~ "badge-sm"
- assert html =~ "Small"
- end
-
- test "renders as span (non-interactive)" do
- assigns = %{}
-
- html =
- rendered_to_string(~H"""
- <.badge variant="info">Info
- """)
-
- assert html =~ ~r/
]*class="[^"]*badge[^"]*"/
- refute html =~ ~r/ (suspended uses warning to match edit button)" do
- assert MembershipFeeHelpers.status_variant(:paid) == :success
- assert MembershipFeeHelpers.status_variant(:unpaid) == :error
- assert MembershipFeeHelpers.status_variant(:suspended) == :warning
- end
- end
-
describe "status_color/1" do
test "returns correct color classes for statuses" do
assert MembershipFeeHelpers.status_color(:paid) == "badge-success"
diff --git a/test/mv_web/live/custom_field_live/deletion_test.exs b/test/mv_web/live/custom_field_live/deletion_test.exs
index 962ada1..5ec955e 100644
--- a/test/mv_web/live/custom_field_live/deletion_test.exs
+++ b/test/mv_web/live/custom_field_live/deletion_test.exs
@@ -46,19 +46,15 @@ defmodule MvWeb.CustomFieldLive.DeletionTest do
%{conn: conn, user: user_with_role}
end
- # Delete is in the edit form (FormComponent). First row click opens form (overview) or switches
- # to edit-mode (new component shows table). If delete button is visible, click it; else click row
- # again to open the form, then click delete.
+ # Delete is in the edit form (FormComponent); open form by clicking the name cell (unique td with phx-click)
defp open_delete_modal(view, custom_field) do
- row_selector = "tr#custom_fields-#{custom_field.id} td"
- view |> element(row_selector, custom_field.name) |> render_click()
+ view
+ |> element("tr#custom_fields-#{custom_field.id} td", custom_field.name)
+ |> render_click()
- if has_element?(view, "[data-testid=custom-field-delete]") do
- view |> element("[data-testid=custom-field-delete]") |> render_click()
- else
- view |> element(row_selector, custom_field.name) |> render_click()
- view |> element("[data-testid=custom-field-delete]") |> render_click()
- end
+ view
+ |> element("[data-testid=custom-field-delete]")
+ |> render_click()
end
describe "delete button and modal" do
@@ -75,12 +71,8 @@ defmodule MvWeb.CustomFieldLive.DeletionTest do
# Modal should be visible
assert has_element?(view, "#delete-custom-field-modal")
- # Edit mode: section titles must not reappear when modal opens (regression)
- refute has_element?(view, "h2", "Member fields")
- refute has_element?(view, "h2", "Custom fields")
-
# Should show correct member count (1 member)
- assert render(view) =~ "1 member has a value assigned for this datafield"
+ assert render(view) =~ "1 member has a value assigned for this custom field"
# Should show the slug
assert render(view) =~ custom_field.slug
@@ -99,7 +91,7 @@ defmodule MvWeb.CustomFieldLive.DeletionTest do
open_delete_modal(view, custom_field)
# Should show plural form
- assert render(view) =~ "2 members have values assigned for this datafield"
+ assert render(view) =~ "2 members have values assigned for this custom field"
end
test "shows 0 members for custom field without values", %{conn: conn} do
@@ -109,7 +101,7 @@ defmodule MvWeb.CustomFieldLive.DeletionTest do
open_delete_modal(view, custom_field)
# Should show 0 members
- assert render(view) =~ "0 members have values assigned for this datafield"
+ assert render(view) =~ "0 members have values assigned for this custom field"
end
end
diff --git a/test/mv_web/live/member_field_live/index_component_test.exs b/test/mv_web/live/member_field_live/index_component_test.exs
index 4356279..d3c1612 100644
--- a/test/mv_web/live/member_field_live/index_component_test.exs
+++ b/test/mv_web/live/member_field_live/index_component_test.exs
@@ -83,21 +83,6 @@ defmodule MvWeb.MemberFieldLive.IndexComponentTest do
end
end
- describe "edit mode visibility" do
- test "clicking member field row shows only form, no section titles", %{conn: conn} do
- {:ok, view, _html} = live(conn, ~p"/admin/datafields")
-
- # Row click is on the first td (no col_click); click that cell to open edit form
- view
- |> element("tr#member_field-first_name td:first-child")
- |> render_click()
-
- assert has_element?(view, "#member-field-form-first_name")
- refute has_element?(view, "h2", "Custom fields")
- refute has_element?(view, "h2", "Member fields")
- end
- end
-
describe "required fields" do
setup do
{:ok, settings} = Membership.get_settings()
diff --git a/test/mv_web/live/role_live_test.exs b/test/mv_web/live/role_live_test.exs
index 2bbb443..57ce814 100644
--- a/test/mv_web/live/role_live_test.exs
+++ b/test/mv_web/live/role_live_test.exs
@@ -386,16 +386,11 @@ defmodule MvWeb.RoleLiveTest do
{:ok, view, _html} = live(conn, "/admin/roles/#{role.id}")
- # Open delete modal from Danger zone
+ # Delete from Danger zone on show page
view
|> element("[data-testid=role-delete]")
|> render_click()
- # Confirm deletion in modal
- view
- |> element("[data-testid=role-delete-confirm]")
- |> render_click()
-
assert_redirect(view, "/admin/roles")
# Verify deletion by checking database
diff --git a/test/mv_web/live/statistics_live_test.exs b/test/mv_web/live/statistics_live_test.exs
index 49c4167..ed6128f 100644
--- a/test/mv_web/live/statistics_live_test.exs
+++ b/test/mv_web/live/statistics_live_test.exs
@@ -29,8 +29,9 @@ defmodule MvWeb.StatisticsLiveTest do
test "page shows overview of all relevant years without year selector", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/statistics")
- # Page shows multi-year data (member numbers by year) and year column; no single-year selector as main control
- assert html =~ "Member numbers by year"
+ # No year dropdown: single select for year should not be present as main control
+ assert html =~ "Overview" or html =~ "overview"
+ # table header or legend
assert html =~ "Year"
end
diff --git a/test/mv_web/member_live/index_groups_display_test.exs b/test/mv_web/member_live/index_groups_display_test.exs
index 263ac2a..b28b978 100644
--- a/test/mv_web/member_live/index_groups_display_test.exs
+++ b/test/mv_web/member_live/index_groups_display_test.exs
@@ -95,20 +95,6 @@ defmodule MvWeb.MemberLive.IndexGroupsDisplayTest do
assert html =~ member3.first_name
end
- test "empty group cell is visually empty with sr-only text (no dash)", %{
- conn: conn,
- member3: member3
- } do
- conn = conn_with_oidc_user(conn)
- {:ok, _view, html} = live(conn, "/members")
- assert html =~ member3.first_name
- # Screen reader gets a meaningful label for the empty cell
- assert html =~ "sr-only"
- assert html =~ "No group assignment"
- # No visible dash as placeholder (Design Guidelines §8.6)
- refute html =~ ~r/]*class="[^"]*text-base-content\/50[^"]*"[^>]*>—<\/span>/
- end
-
test "displays group name correctly in badge", %{conn: conn, group1: group1} do
conn = conn_with_oidc_user(conn)
{:ok, _view, html} = live(conn, "/members")
diff --git a/test/mv_web/user_live/index_test.exs b/test/mv_web/user_live/index_test.exs
index c0be795..f748000 100644
--- a/test/mv_web/user_live/index_test.exs
+++ b/test/mv_web/user_live/index_test.exs
@@ -123,17 +123,13 @@ defmodule MvWeb.UserLive.IndexTest do
{:ok, index_view, _html} = live(conn, "/users")
assert render(index_view) =~ "delete-me@example.com"
- # Navigate to user show, open delete modal, then confirm in modal (WCAG modal pattern)
+ # Navigate to user show and trigger delete from Danger zone
{:ok, show_view, _html} = live(conn, "/users/#{user.id}")
show_view
|> element("[data-testid=user-delete]")
|> render_click()
- show_view
- |> element("#delete-user-modal button", "Delete")
- |> render_click()
-
# Should redirect to index
assert_redirect(show_view, "/users")
@@ -210,9 +206,7 @@ defmodule MvWeb.UserLive.IndexTest do
end
describe "Password column display" do
- test "user without password shows empty cell with sr-only text in Password column", %{
- conn: conn
- } do
+ test "user without password shows em dash in Password column", %{conn: conn} do
# User created with hashed_password: nil (no password) - must not get default password
user_no_pw =
create_test_user(%{
@@ -225,13 +219,9 @@ defmodule MvWeb.UserLive.IndexTest do
assert html =~ "no-password@example.com"
- # Password column: visually empty, screen-reader gets "Not set" (Design Guidelines §8.6)
+ # Password column must show "—" (em dash) for user without password, not "Enabled"
row = view |> element("tr#row-#{user_no_pw.id}") |> render()
- assert row =~ "sr-only", "Password column should have sr-only text for accessibility"
- assert row =~ "Not set", "Screen reader should get 'Not set' for empty password"
-
- refute row =~ "—",
- "Password column must not show dash (use empty cell + sr-only per CODE_GUIDELINES §8)"
+ assert row =~ "—", "Password column should show em dash for user without password"
refute row =~ "Enabled",
"Password column must not show Enabled when user has no password"