Merge pull request 'Adds Global Settings closes #211' (#219) from feature/211_globalsettings into main
Some checks failed
continuous-integration/drone/push Build is failing

Reviewed-on: #219
Reviewed-by: moritz <moritz@noreply.git.local-it.org>
This commit is contained in:
carla 2025-12-01 10:57:04 +01:00
commit e803dbdf8b
17 changed files with 822 additions and 103 deletions

View file

@ -8,6 +8,9 @@ TOKEN_SIGNING_SECRET=changeme-run-mix-phx.gen.secret
# Required: Hostname for URL generation # Required: Hostname for URL generation
PHX_HOST=localhost PHX_HOST=localhost
# Recommended: Association settings
ASSOCIATION_NAME="Sportsclub XYZ"
# Optional: OIDC Configuration # Optional: OIDC Configuration
# These have defaults in docker-compose.prod.yml, only override if needed # These have defaults in docker-compose.prod.yml, only override if needed
# OIDC_CLIENT_ID=mv # OIDC_CLIENT_ID=mv

View file

@ -6,12 +6,14 @@ defmodule Mv.Membership do
- `Member` - Club members with personal information and custom field values - `Member` - Club members with personal information and custom field values
- `CustomFieldValue` - Dynamic custom field values attached to members - `CustomFieldValue` - Dynamic custom field values attached to members
- `CustomField` - Schema definitions for custom fields - `CustomField` - Schema definitions for custom fields
- `Setting` - Global application settings (singleton)
## Public API ## Public API
The domain exposes these main actions: The domain exposes these main actions:
- Member CRUD: `create_member/1`, `list_members/0`, `update_member/2`, `destroy_member/1` - Member CRUD: `create_member/1`, `list_members/0`, `update_member/2`, `destroy_member/1`
- Custom field value management: `create_custom_field_value/1`, `list_custom_field_values/0`, etc. - Custom field value management: `create_custom_field_value/1`, `list_custom_field_values/0`, etc.
- Custom field management: `create_custom_field/1`, `list_custom_fields/0`, etc. - Custom field management: `create_custom_field/1`, `list_custom_fields/0`, etc.
- Settings management: `get_settings/0`, `update_settings/2`
## Admin Interface ## Admin Interface
The domain is configured with AshAdmin for management UI. The domain is configured with AshAdmin for management UI.
@ -45,5 +47,80 @@ defmodule Mv.Membership do
define :destroy_custom_field, action: :destroy_with_values define :destroy_custom_field, action: :destroy_with_values
define :prepare_custom_field_deletion, action: :prepare_deletion, args: [:id] define :prepare_custom_field_deletion, action: :prepare_deletion, args: [:id]
end end
resource Mv.Membership.Setting do
# Note: create action exists but is not exposed via code interface
# It's only used internally as fallback in get_settings/0
# Settings should be created via seed script
define :update_settings, action: :update
end
end
# Singleton pattern: Get the single settings record
@doc """
Gets the global settings.
Settings should normally be created via the seed script (`priv/repo/seeds.exs`).
If no settings exist, this function will create them as a fallback using the
`ASSOCIATION_NAME` environment variable or "Club Name" as default.
## Returns
- `{:ok, settings}` - The settings record
- `{:ok, nil}` - No settings exist (should not happen if seeds were run)
- `{:error, error}` - Error reading settings
## Examples
iex> {:ok, settings} = Mv.Membership.get_settings()
iex> settings.club_name
"My Club"
"""
def get_settings do
# Try to get the first (and only) settings record
case Ash.read_one(Mv.Membership.Setting, domain: __MODULE__) do
{:ok, nil} ->
# No settings exist - create as fallback (should normally be created via seed script)
default_club_name = System.get_env("ASSOCIATION_NAME") || "Club Name"
Mv.Membership.Setting
|> Ash.Changeset.for_create(:create, %{club_name: default_club_name})
|> Ash.create!(domain: __MODULE__)
|> then(fn settings -> {:ok, settings} end)
{:ok, settings} ->
{:ok, settings}
{:error, error} ->
{:error, error}
end
end
@doc """
Updates the global settings.
## Parameters
- `settings` - The settings record to update
- `attrs` - A map of attributes to update (e.g., `%{club_name: "New Name"}`)
## Returns
- `{:ok, updated_settings}` - Successfully updated settings
- `{:error, error}` - Validation or update error
## Examples
iex> {:ok, settings} = Mv.Membership.get_settings()
iex> {:ok, updated} = Mv.Membership.update_settings(settings, %{club_name: "New Club"})
iex> updated.club_name
"New Club"
"""
def update_settings(settings, attrs) do
settings
|> Ash.Changeset.for_update(:update, attrs)
|> Ash.update(domain: __MODULE__)
end end
end end

80
lib/membership/setting.ex Normal file
View file

@ -0,0 +1,80 @@
defmodule Mv.Membership.Setting do
@moduledoc """
Ash resource representing global application settings.
## Overview
Settings is a singleton resource that stores global configuration for the association,
such as the club name and branding information. There should only ever be one settings
record in the database.
## Attributes
- `club_name` - The name of the association/club (required, cannot be empty)
## Singleton Pattern
This resource uses a singleton pattern - there should only be one settings record.
The resource is designed to be read and updated, but not created or destroyed
through normal CRUD operations. Initial settings should be seeded.
## Environment Variable Support
The `club_name` can be set via the `ASSOCIATION_NAME` environment variable.
If set, the environment variable value is used as a fallback when no database
value exists. Database values always take precedence over environment variables.
## Examples
# Get current settings
{:ok, settings} = Mv.Membership.get_settings()
settings.club_name # => "My Club"
# Update club name
{:ok, updated} = Mv.Membership.update_settings(settings, %{club_name: "New Name"})
"""
use Ash.Resource,
domain: Mv.Membership,
data_layer: AshPostgres.DataLayer
postgres do
table "settings"
repo Mv.Repo
end
resource do
description "Global application settings (singleton resource)"
end
actions do
defaults [:read]
# Internal create action - not exposed via code interface
# Used only as fallback in get_settings/0 if settings don't exist
# Settings should normally be created via seed script
create :create do
accept [:club_name]
end
update :update do
primary? true
accept [:club_name]
end
end
validations do
validate present(:club_name), on: [:create, :update]
validate string_length(:club_name, min: 1), on: [:create, :update]
end
attributes do
uuid_primary_key :id
attribute :club_name, :string,
allow_nil?: false,
public?: true,
description: "The name of the association/club",
constraints: [
trim?: true,
min_length: 1
]
timestamps()
end
end

View file

@ -6,15 +6,21 @@ defmodule MvWeb.Layouts.Navbar do
use Gettext, backend: MvWeb.Gettext use Gettext, backend: MvWeb.Gettext
use MvWeb, :verified_routes use MvWeb, :verified_routes
alias Mv.Membership
attr :current_user, :map, attr :current_user, :map,
required: true, required: true,
doc: "The current user - navbar is only shown when user is present" doc: "The current user - navbar is only shown when user is present"
def navbar(assigns) do def navbar(assigns) do
club_name = get_club_name()
assigns = assign(assigns, :club_name, club_name)
~H""" ~H"""
<header class="navbar bg-base-100 shadow-sm"> <header class="navbar bg-base-100 shadow-sm">
<div class="flex-1"> <div class="flex-1">
<a class="btn btn-ghost text-xl">Mitgliederverwaltung</a> <a class="btn btn-ghost text-xl">{@club_name}</a>
<ul class="menu menu-horizontal bg-base-200"> <ul class="menu menu-horizontal bg-base-200">
<li><.link navigate="/members">{gettext("Members")}</.link></li> <li><.link navigate="/members">{gettext("Members")}</.link></li>
<li><.link navigate="/custom_fields">{gettext("Custom Fields")}</.link></li> <li><.link navigate="/custom_fields">{gettext("Custom Fields")}</.link></li>
@ -89,7 +95,9 @@ defmodule MvWeb.Layouts.Navbar do
{gettext("Profil")} {gettext("Profil")}
</.link> </.link>
</li> </li>
<li><a>{gettext("Settings")}</a></li> <li>
<.link navigate={~p"/settings"}>{gettext("Settings")}</.link>
</li>
<li> <li>
<.link href={~p"/sign-out"}>{gettext("Logout")}</.link> <.link href={~p"/sign-out"}>{gettext("Logout")}</.link>
</li> </li>
@ -99,4 +107,13 @@ defmodule MvWeb.Layouts.Navbar do
</header> </header>
""" """
end end
# Helper function to get club name from settings
# Falls back to "Mitgliederverwaltung" if settings can't be loaded
defp get_club_name do
case Membership.get_settings() do
{:ok, settings} -> settings.club_name
_ -> "Mitgliederverwaltung"
end
end
end end

View file

@ -0,0 +1,97 @@
defmodule MvWeb.GlobalSettingsLive do
@moduledoc """
LiveView for managing global application settings (Vereinsdaten).
## Features
- Edit the association/club name
- Real-time form validation
- Success/error feedback
## Settings
- `club_name` - The name of the association/club (required)
## Events
- `validate` - Real-time form validation
- `save` - Save settings changes
## Note
Settings is a singleton resource - there is only one settings record.
The club_name can also be set via the `ASSOCIATION_NAME` environment variable.
"""
use MvWeb, :live_view
alias Mv.Membership
@impl true
def mount(_params, _session, socket) do
{:ok, settings} = Membership.get_settings()
{:ok,
socket
|> assign(:page_title, gettext("Club Settings"))
|> assign(:settings, settings)
|> assign_form()}
end
@impl true
def render(assigns) do
~H"""
<Layouts.app flash={@flash} current_user={@current_user}>
<.header>
{gettext("Club Settings")}
<:subtitle>
{gettext("Manage global settings for the association.")}
</:subtitle>
</.header>
<.form for={@form} id="settings-form" phx-change="validate" phx-submit="save">
<.input
field={@form[:club_name]}
type="text"
label={gettext("Association Name")}
required
/>
<.button phx-disable-with={gettext("Saving...")} variant="primary">
{gettext("Save Settings")}
</.button>
</.form>
</Layouts.app>
"""
end
@impl true
def handle_event("validate", %{"setting" => setting_params}, socket) do
{:noreply,
assign(socket, form: AshPhoenix.Form.validate(socket.assigns.form, setting_params))}
end
def handle_event("save", %{"setting" => setting_params}, socket) do
case AshPhoenix.Form.submit(socket.assigns.form, params: setting_params) do
{:ok, updated_settings} ->
socket =
socket
|> assign(:settings, updated_settings)
|> put_flash(:info, gettext("Settings updated successfully"))
|> assign_form()
{:noreply, socket}
{:error, form} ->
{:noreply, assign(socket, form: form)}
end
end
defp assign_form(%{assigns: %{settings: settings}} = socket) do
form =
AshPhoenix.Form.for_update(
settings,
:update,
api: Membership,
as: "setting",
forms: [auto?: true]
)
assign(socket, form: to_form(form))
end
end

View file

@ -73,6 +73,8 @@ defmodule MvWeb.Router do
live "/users/:id", UserLive.Show, :show live "/users/:id", UserLive.Show, :show
live "/users/:id/show/edit", UserLive.Show, :edit live "/users/:id/show/edit", UserLive.Show, :edit
live "/settings", GlobalSettingsLive
post "/set_locale", LocaleController, :set_locale post "/set_locale", LocaleController, :set_locale
end end

View file

@ -16,7 +16,7 @@ msgid "Actions"
msgstr "Aktionen" msgstr "Aktionen"
#: lib/mv_web/live/member_live/index.html.heex:202 #: lib/mv_web/live/member_live/index.html.heex:202
#: lib/mv_web/live/user_live/index.html.heex:65 #: lib/mv_web/live/user_live/index.html.heex:72
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Are you sure?" msgid "Are you sure?"
msgstr "Bist du sicher?" msgstr "Bist du sicher?"
@ -35,14 +35,14 @@ msgid "City"
msgstr "Stadt" msgstr "Stadt"
#: lib/mv_web/live/member_live/index.html.heex:204 #: lib/mv_web/live/member_live/index.html.heex:204
#: lib/mv_web/live/user_live/index.html.heex:67 #: lib/mv_web/live/user_live/index.html.heex:74
#, 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:196 #: lib/mv_web/live/member_live/index.html.heex:196
#: lib/mv_web/live/user_live/form.ex:141 #: lib/mv_web/live/user_live/form.ex:265
#: lib/mv_web/live/user_live/index.html.heex:59 #: lib/mv_web/live/user_live/index.html.heex:66
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Edit" msgid "Edit"
msgstr "Bearbeite" msgstr "Bearbeite"
@ -88,7 +88,7 @@ msgid "New Member"
msgstr "Neues Mitglied" msgstr "Neues Mitglied"
#: lib/mv_web/live/member_live/index.html.heex:193 #: lib/mv_web/live/member_live/index.html.heex:193
#: lib/mv_web/live/user_live/index.html.heex:56 #: lib/mv_web/live/user_live/index.html.heex:63
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Show" msgid "Show"
msgstr "Anzeigen" msgstr "Anzeigen"
@ -160,8 +160,9 @@ msgstr "Mitglied speichern"
#: lib/mv_web/live/custom_field_live/form.ex:66 #: lib/mv_web/live/custom_field_live/form.ex:66
#: lib/mv_web/live/custom_field_value_live/form.ex:74 #: lib/mv_web/live/custom_field_value_live/form.ex:74
#: lib/mv_web/live/global_settings_live.ex:55
#: lib/mv_web/live/member_live/form.ex:79 #: lib/mv_web/live/member_live/form.ex:79
#: lib/mv_web/live/user_live/form.ex:234 #: lib/mv_web/live/user_live/form.ex:248
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Saving..." msgid "Saving..."
msgstr "Speichern..." msgstr "Speichern..."
@ -183,7 +184,7 @@ msgstr "Dieses Formular dient zur Verwaltung von Mitgliedern und deren Eigenscha
msgid "Id" msgid "Id"
msgstr "ID" msgstr "ID"
#: lib/mv_web/live/member_live/index/formatter.ex:65 #: lib/mv_web/live/member_live/index/formatter.ex:61
#: lib/mv_web/live/member_live/show.ex:53 #: lib/mv_web/live/member_live/show.ex:53
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "No" msgid "No"
@ -199,7 +200,7 @@ msgstr "Mitglied anzeigen"
msgid "This is a member record from your database." msgid "This is a member record from your database."
msgstr "Dies ist ein Mitglied aus deiner Datenbank." msgstr "Dies ist ein Mitglied aus deiner Datenbank."
#: lib/mv_web/live/member_live/index/formatter.ex:64 #: lib/mv_web/live/member_live/index/formatter.ex:60
#: lib/mv_web/live/member_live/show.ex:53 #: lib/mv_web/live/member_live/show.ex:53
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Yes" msgid "Yes"
@ -258,7 +259,7 @@ msgstr "Ihr Passwort wurde erfolgreich zurückgesetzt"
#: lib/mv_web/live/custom_field_live/index.ex:120 #: lib/mv_web/live/custom_field_live/index.ex:120
#: lib/mv_web/live/custom_field_value_live/form.ex:77 #: lib/mv_web/live/custom_field_value_live/form.ex:77
#: lib/mv_web/live/member_live/form.ex:82 #: lib/mv_web/live/member_live/form.ex:82
#: lib/mv_web/live/user_live/form.ex:237 #: lib/mv_web/live/user_live/form.ex:251
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Cancel" msgid "Cancel"
msgstr "Abbrechen" msgstr "Abbrechen"
@ -293,7 +294,7 @@ msgstr "ID"
msgid "Immutable" msgid "Immutable"
msgstr "Unveränderlich" msgstr "Unveränderlich"
#: lib/mv_web/components/layouts/navbar.ex:94 #: lib/mv_web/components/layouts/navbar.ex:102
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Logout" msgid "Logout"
msgstr "Abmelden" msgstr "Abmelden"
@ -309,7 +310,7 @@ msgstr "Benutzer*innen auflisten"
msgid "Member" msgid "Member"
msgstr "Mitglied" msgstr "Mitglied"
#: lib/mv_web/components/layouts/navbar.ex:19 #: lib/mv_web/components/layouts/navbar.ex:25
#: lib/mv_web/live/member_live/index.ex:57 #: lib/mv_web/live/member_live/index.ex:57
#: 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
@ -338,7 +339,7 @@ msgstr "Nicht gesetzt"
#: lib/mv_web/live/user_live/form.ex:107 #: lib/mv_web/live/user_live/form.ex:107
#: lib/mv_web/live/user_live/form.ex:115 #: lib/mv_web/live/user_live/form.ex:115
#: lib/mv_web/live/user_live/form.ex:210 #: lib/mv_web/live/user_live/form.ex:224
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Note" msgid "Note"
msgstr "Hinweis" msgstr "Hinweis"
@ -354,7 +355,7 @@ msgstr "OIDC ID"
msgid "Password Authentication" msgid "Password Authentication"
msgstr "Passwort-Authentifizierung" msgstr "Passwort-Authentifizierung"
#: lib/mv_web/components/layouts/navbar.ex:89 #: lib/mv_web/components/layouts/navbar.ex:95
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Profil" msgid "Profil"
msgstr "Profil" msgstr "Profil"
@ -374,12 +375,12 @@ 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:92 #: lib/mv_web/components/layouts/navbar.ex:99
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Settings" msgid "Settings"
msgstr "Einstellungen" msgstr "Einstellungen"
#: lib/mv_web/live/user_live/form.ex:235 #: lib/mv_web/live/user_live/form.ex:249
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Save User" msgid "Save User"
msgstr "Benutzer*in speichern" msgstr "Benutzer*in speichern"
@ -404,7 +405,7 @@ msgstr "Nicht unterstützter Wertetyp: %{type}"
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*innen-Datensätze zu verwalten." msgstr "Verwenden Sie dieses Formular, um Benutzer*innen-Datensätze zu verwalten."
#: lib/mv_web/live/user_live/form.ex:252 #: lib/mv_web/live/user_live/form.ex:266
#: lib/mv_web/live/user_live/show.ex:34 #: lib/mv_web/live/user_live/show.ex:34
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "User" msgid "User"
@ -432,7 +433,7 @@ msgstr "aufsteigend"
msgid "descending" msgid "descending"
msgstr "absteigend" msgstr "absteigend"
#: lib/mv_web/live/user_live/form.ex:251 #: lib/mv_web/live/user_live/form.ex:265
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "New" msgid "New"
msgstr "Neue*r" msgstr "Neue*r"
@ -542,14 +543,14 @@ msgstr "Zurück zur Mitgliederliste"
msgid "Back to users list" msgid "Back to users list"
msgstr "Zurück zur Benutzer*innen-Liste" msgstr "Zurück zur Benutzer*innen-Liste"
#: lib/mv_web/components/layouts/navbar.ex:27
#: lib/mv_web/components/layouts/navbar.ex:33 #: lib/mv_web/components/layouts/navbar.ex:33
#: lib/mv_web/components/layouts/navbar.ex:39
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Select language" msgid "Select language"
msgstr "Sprache auswählen" msgstr "Sprache auswählen"
#: lib/mv_web/components/layouts/navbar.ex:40 #: lib/mv_web/components/layouts/navbar.ex:46
#: lib/mv_web/components/layouts/navbar.ex:60 #: lib/mv_web/components/layouts/navbar.ex:66
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Toggle dark mode" msgid "Toggle dark mode"
msgstr "Dunklen Modus umschalten" msgstr "Dunklen Modus umschalten"
@ -560,7 +561,7 @@ msgstr "Dunklen Modus umschalten"
msgid "Search..." msgid "Search..."
msgstr "Suchen..." msgstr "Suchen..."
#: lib/mv_web/components/layouts/navbar.ex:21 #: lib/mv_web/components/layouts/navbar.ex:27
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Users" msgid "Users"
msgstr "Benutzer*innen" msgstr "Benutzer*innen"
@ -653,7 +654,7 @@ msgstr "Benutzerdefinierten Feldwert speichern"
msgid "Use this form to manage custom_field records in your database." msgid "Use this form to manage custom_field records in your database."
msgstr "Verwende dieses Formular, um Benutzerdefinierte Felder in deiner Datenbank zu verwalten." msgstr "Verwende dieses Formular, um Benutzerdefinierte Felder in deiner Datenbank zu verwalten."
#: lib/mv_web/components/layouts/navbar.ex:20 #: lib/mv_web/components/layouts/navbar.ex:26
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Custom Fields" msgid "Custom Fields"
msgstr "Benutzerdefinierte Felder" msgstr "Benutzerdefinierte Felder"
@ -700,37 +701,73 @@ msgstr "Obigen Text zur Bestätigung eingeben"
msgid "To confirm deletion, please enter this text:" msgid "To confirm deletion, please enter this text:"
msgstr "Um die Löschung zu bestätigen, gib bitte folgenden Text ein:" msgstr "Um die Löschung zu bestätigen, gib bitte folgenden Text ein:"
#: lib/mv_web/live/user_live/form.ex:210 #: lib/mv_web/live/custom_field_live/form.ex:64
#, elixir-autogen, elixir-format
msgid "Show in overview"
msgstr "In der Mitglieder-Übersicht anzeigen"
#: lib/mv_web/live/global_settings_live.ex:51
#, elixir-autogen, elixir-format
msgid "Association Name"
msgstr "Vereinsname"
#: lib/mv_web/live/global_settings_live.ex:31
#: lib/mv_web/live/global_settings_live.ex:41
#, elixir-autogen, elixir-format, fuzzy
msgid "Club Settings"
msgstr "Vereinsdaten"
#: lib/mv_web/live/global_settings_live.ex:43
#, elixir-autogen, elixir-format
msgid "Manage global settings for the association."
msgstr "Passe übergreifende Einstellungen für den Verein an."
#: lib/mv_web/live/global_settings_live.ex:56
#, elixir-autogen, elixir-format, fuzzy
msgid "Save Settings"
msgstr "Einstellungen speichern"
#: lib/mv_web/live/global_settings_live.ex:75
#, elixir-autogen, elixir-format
msgid "Settings updated successfully"
msgstr "Einstellungen erfolgreich gespeichert"
#: lib/mv_web/live/user_live/form.ex:224
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "A member with this email already exists. To link with a different member, please change one of the email addresses first." msgid "A member with this email already exists. To link with a different member, please change one of the email addresses first."
msgstr "Ein Mitglied mit dieser E-Mail-Adresse existiert bereits. Um mit einem anderen Mitglied zu verknüpfen, ändern Sie bitte zuerst eine der E-Mail-Adressen." msgstr "Ein Mitglied mit dieser E-Mail-Adresse existiert bereits. Um mit einem anderen Mitglied zu verknüpfen, ändern Sie bitte zuerst eine der E-Mail-Adressen."
#: lib/mv_web/live/user_live/form.ex:185 #: lib/mv_web/live/user_live/form.ex:192
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Available members" msgid "Available members"
msgstr "Verfügbare Mitglieder" msgstr "Verfügbare Mitglieder"
#: lib/mv_web/live/user_live/form.ex:357
#, elixir-autogen, elixir-format
msgid "Failed to link member: %{error}"
msgstr "Fehler beim Verlinken des Mitglieds: %{error}"
#: lib/mv_web/live/user_live/form.ex:152 #: lib/mv_web/live/user_live/form.ex:152
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Member will be unlinked when you save. Cannot select new member until saved." msgid "Member will be unlinked when you save. Cannot select new member until saved."
msgstr "Mitglied wird beim Speichern entverknüpft. Neues Mitglied kann erst nach dem Speichern ausgewählt werden." msgstr "Mitglied wird beim Speichern entverknüpft. Neues Mitglied kann erst nach dem Speichern ausgewählt werden."
#: lib/mv_web/live/user_live/form.ex:226 #: lib/mv_web/live/user_live/form.ex:240
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Save to confirm linking." msgid "Save to confirm linking."
msgstr "Speichern, um die Verknüpfung zu bestätigen." msgstr "Speichern, um die Verknüpfung zu bestätigen."
#: lib/mv_web/live/user_live/form.ex:169 #: lib/mv_web/live/user_live/form.ex:171
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Search for a member to link..." msgid "Search for a member to link..."
msgstr "Nach einem Mitglied zum Verknüpfen suchen..." msgstr "Nach einem Mitglied zum Verknüpfen suchen..."
#: lib/mv_web/live/user_live/form.ex:173 #: lib/mv_web/live/user_live/form.ex:175
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Search for member to link" msgid "Search for member to link"
msgstr "Nach Mitglied zum Verknüpfen suchen" msgstr "Nach Mitglied zum Verknüpfen suchen"
#: lib/mv_web/live/user_live/form.ex:223 #: lib/mv_web/live/user_live/form.ex:237
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Selected" msgid "Selected"
msgstr "Ausgewählt" msgstr "Ausgewählt"
@ -745,15 +782,6 @@ msgstr "Mitglied entverknüpfen"
msgid "Unlinking scheduled" msgid "Unlinking scheduled"
msgstr "Entverknüpfung geplant" msgstr "Entverknüpfung geplant"
#: lib/mv_web/live/user_live/form.ex:342
#, elixir-autogen, elixir-format
msgid "Failed to link member: %{error}"
msgstr ""
#: lib/mv_web/live/custom_field_live/form.ex:64
#, elixir-autogen, elixir-format
msgid "Show in overview"
msgstr "In der Mitglieder-Übersicht anzeigen"
#~ #: lib/mv_web/live/custom_field_live/index.ex:97 #~ #: lib/mv_web/live/custom_field_live/index.ex:97
#~ #, elixir-autogen, elixir-format #~ #, elixir-autogen, elixir-format
#~ msgid "To confirm deletion, please enter the custom field slug:" #~ msgid "To confirm deletion, please enter the custom field slug:"

View file

@ -17,7 +17,7 @@ msgid "Actions"
msgstr "" msgstr ""
#: lib/mv_web/live/member_live/index.html.heex:202 #: lib/mv_web/live/member_live/index.html.heex:202
#: lib/mv_web/live/user_live/index.html.heex:65 #: lib/mv_web/live/user_live/index.html.heex:72
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Are you sure?" msgid "Are you sure?"
msgstr "" msgstr ""
@ -36,14 +36,14 @@ msgid "City"
msgstr "" msgstr ""
#: lib/mv_web/live/member_live/index.html.heex:204 #: lib/mv_web/live/member_live/index.html.heex:204
#: lib/mv_web/live/user_live/index.html.heex:67 #: lib/mv_web/live/user_live/index.html.heex:74
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Delete" msgid "Delete"
msgstr "" msgstr ""
#: lib/mv_web/live/member_live/index.html.heex:196 #: lib/mv_web/live/member_live/index.html.heex:196
#: lib/mv_web/live/user_live/form.ex:141 #: lib/mv_web/live/user_live/form.ex:265
#: lib/mv_web/live/user_live/index.html.heex:59 #: lib/mv_web/live/user_live/index.html.heex:66
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Edit" msgid "Edit"
msgstr "" msgstr ""
@ -89,7 +89,7 @@ msgid "New Member"
msgstr "" msgstr ""
#: lib/mv_web/live/member_live/index.html.heex:193 #: lib/mv_web/live/member_live/index.html.heex:193
#: lib/mv_web/live/user_live/index.html.heex:56 #: lib/mv_web/live/user_live/index.html.heex:63
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Show" msgid "Show"
msgstr "" msgstr ""
@ -161,8 +161,9 @@ msgstr ""
#: lib/mv_web/live/custom_field_live/form.ex:66 #: lib/mv_web/live/custom_field_live/form.ex:66
#: lib/mv_web/live/custom_field_value_live/form.ex:74 #: lib/mv_web/live/custom_field_value_live/form.ex:74
#: lib/mv_web/live/global_settings_live.ex:55
#: lib/mv_web/live/member_live/form.ex:79 #: lib/mv_web/live/member_live/form.ex:79
#: lib/mv_web/live/user_live/form.ex:234 #: lib/mv_web/live/user_live/form.ex:248
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Saving..." msgid "Saving..."
msgstr "" msgstr ""
@ -184,7 +185,7 @@ msgstr ""
msgid "Id" msgid "Id"
msgstr "" msgstr ""
#: lib/mv_web/live/member_live/index/formatter.ex:65 #: lib/mv_web/live/member_live/index/formatter.ex:61
#: lib/mv_web/live/member_live/show.ex:53 #: lib/mv_web/live/member_live/show.ex:53
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "No" msgid "No"
@ -200,7 +201,7 @@ msgstr ""
msgid "This is a member record from your database." msgid "This is a member record from your database."
msgstr "" msgstr ""
#: lib/mv_web/live/member_live/index/formatter.ex:64 #: lib/mv_web/live/member_live/index/formatter.ex:60
#: lib/mv_web/live/member_live/show.ex:53 #: lib/mv_web/live/member_live/show.ex:53
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Yes" msgid "Yes"
@ -259,7 +260,7 @@ msgstr ""
#: lib/mv_web/live/custom_field_live/index.ex:120 #: lib/mv_web/live/custom_field_live/index.ex:120
#: lib/mv_web/live/custom_field_value_live/form.ex:77 #: lib/mv_web/live/custom_field_value_live/form.ex:77
#: lib/mv_web/live/member_live/form.ex:82 #: lib/mv_web/live/member_live/form.ex:82
#: lib/mv_web/live/user_live/form.ex:237 #: lib/mv_web/live/user_live/form.ex:251
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Cancel" msgid "Cancel"
msgstr "" msgstr ""
@ -294,7 +295,7 @@ msgstr ""
msgid "Immutable" msgid "Immutable"
msgstr "" msgstr ""
#: lib/mv_web/components/layouts/navbar.ex:94 #: lib/mv_web/components/layouts/navbar.ex:102
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Logout" msgid "Logout"
msgstr "" msgstr ""
@ -310,7 +311,7 @@ msgstr ""
msgid "Member" msgid "Member"
msgstr "" msgstr ""
#: lib/mv_web/components/layouts/navbar.ex:19 #: lib/mv_web/components/layouts/navbar.ex:25
#: lib/mv_web/live/member_live/index.ex:57 #: lib/mv_web/live/member_live/index.ex:57
#: 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
@ -339,7 +340,7 @@ msgstr ""
#: lib/mv_web/live/user_live/form.ex:107 #: lib/mv_web/live/user_live/form.ex:107
#: lib/mv_web/live/user_live/form.ex:115 #: lib/mv_web/live/user_live/form.ex:115
#: lib/mv_web/live/user_live/form.ex:210 #: lib/mv_web/live/user_live/form.ex:224
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Note" msgid "Note"
msgstr "" msgstr ""
@ -355,7 +356,7 @@ msgstr ""
msgid "Password Authentication" msgid "Password Authentication"
msgstr "" msgstr ""
#: lib/mv_web/components/layouts/navbar.ex:89 #: lib/mv_web/components/layouts/navbar.ex:95
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Profil" msgid "Profil"
msgstr "" msgstr ""
@ -375,12 +376,12 @@ msgstr ""
msgid "Select member" msgid "Select member"
msgstr "" msgstr ""
#: lib/mv_web/components/layouts/navbar.ex:92 #: lib/mv_web/components/layouts/navbar.ex:99
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Settings" msgid "Settings"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:235 #: lib/mv_web/live/user_live/form.ex:249
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Save User" msgid "Save User"
msgstr "" msgstr ""
@ -405,7 +406,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:252 #: lib/mv_web/live/user_live/form.ex:266
#: lib/mv_web/live/user_live/show.ex:34 #: lib/mv_web/live/user_live/show.ex:34
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "User" msgid "User"
@ -433,7 +434,7 @@ msgstr ""
msgid "descending" msgid "descending"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:251 #: lib/mv_web/live/user_live/form.ex:265
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "New" msgid "New"
msgstr "" msgstr ""
@ -543,14 +544,14 @@ msgstr ""
msgid "Back to users list" msgid "Back to users list"
msgstr "" msgstr ""
#: lib/mv_web/components/layouts/navbar.ex:27
#: lib/mv_web/components/layouts/navbar.ex:33 #: lib/mv_web/components/layouts/navbar.ex:33
#: lib/mv_web/components/layouts/navbar.ex:39
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Select language" msgid "Select language"
msgstr "" msgstr ""
#: lib/mv_web/components/layouts/navbar.ex:40 #: lib/mv_web/components/layouts/navbar.ex:46
#: lib/mv_web/components/layouts/navbar.ex:60 #: lib/mv_web/components/layouts/navbar.ex:66
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Toggle dark mode" msgid "Toggle dark mode"
msgstr "" msgstr ""
@ -561,7 +562,7 @@ msgstr ""
msgid "Search..." msgid "Search..."
msgstr "" msgstr ""
#: lib/mv_web/components/layouts/navbar.ex:21 #: lib/mv_web/components/layouts/navbar.ex:27
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Users" msgid "Users"
msgstr "" msgstr ""
@ -654,7 +655,7 @@ msgstr ""
msgid "Use this form to manage custom_field records in your database." msgid "Use this form to manage custom_field records in your database."
msgstr "" msgstr ""
#: lib/mv_web/components/layouts/navbar.ex:20 #: lib/mv_web/components/layouts/navbar.ex:26
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Custom Fields" msgid "Custom Fields"
msgstr "" msgstr ""
@ -705,3 +706,79 @@ msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Show in overview" msgid "Show in overview"
msgstr "" msgstr ""
#: lib/mv_web/live/global_settings_live.ex:51
#, elixir-autogen, elixir-format
msgid "Association Name"
msgstr ""
#: lib/mv_web/live/global_settings_live.ex:31
#: lib/mv_web/live/global_settings_live.ex:41
#, elixir-autogen, elixir-format
msgid "Club Settings"
msgstr ""
#: lib/mv_web/live/global_settings_live.ex:43
#, elixir-autogen, elixir-format
msgid "Manage global settings for the association."
msgstr ""
#: lib/mv_web/live/global_settings_live.ex:56
#, elixir-autogen, elixir-format
msgid "Save Settings"
msgstr ""
#: lib/mv_web/live/global_settings_live.ex:75
#, elixir-autogen, elixir-format
msgid "Settings updated successfully"
msgstr ""
#: lib/mv_web/live/user_live/form.ex:224
#, elixir-autogen, elixir-format
msgid "A member with this email already exists. To link with a different member, please change one of the email addresses first."
msgstr ""
#: lib/mv_web/live/user_live/form.ex:192
#, elixir-autogen, elixir-format
msgid "Available members"
msgstr ""
#: lib/mv_web/live/user_live/form.ex:357
#, elixir-autogen, elixir-format
msgid "Failed to link member: %{error}"
msgstr ""
#: lib/mv_web/live/user_live/form.ex:152
#, elixir-autogen, elixir-format
msgid "Member will be unlinked when you save. Cannot select new member until saved."
msgstr ""
#: lib/mv_web/live/user_live/form.ex:240
#, elixir-autogen, elixir-format
msgid "Save to confirm linking."
msgstr ""
#: lib/mv_web/live/user_live/form.ex:171
#, elixir-autogen, elixir-format
msgid "Search for a member to link..."
msgstr ""
#: lib/mv_web/live/user_live/form.ex:175
#, elixir-autogen, elixir-format
msgid "Search for member to link"
msgstr ""
#: lib/mv_web/live/user_live/form.ex:237
#, elixir-autogen, elixir-format
msgid "Selected"
msgstr ""
#: lib/mv_web/live/user_live/form.ex:143
#, elixir-autogen, elixir-format
msgid "Unlink Member"
msgstr ""
#: lib/mv_web/live/user_live/form.ex:152
#, elixir-autogen, elixir-format
msgid "Unlinking scheduled"
msgstr ""

View file

@ -17,7 +17,7 @@ msgid "Actions"
msgstr "" msgstr ""
#: lib/mv_web/live/member_live/index.html.heex:202 #: lib/mv_web/live/member_live/index.html.heex:202
#: lib/mv_web/live/user_live/index.html.heex:65 #: lib/mv_web/live/user_live/index.html.heex:72
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Are you sure?" msgid "Are you sure?"
msgstr "" msgstr ""
@ -36,14 +36,14 @@ msgid "City"
msgstr "" msgstr ""
#: lib/mv_web/live/member_live/index.html.heex:204 #: lib/mv_web/live/member_live/index.html.heex:204
#: lib/mv_web/live/user_live/index.html.heex:67 #: lib/mv_web/live/user_live/index.html.heex:74
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Delete" msgid "Delete"
msgstr "" msgstr ""
#: lib/mv_web/live/member_live/index.html.heex:196 #: lib/mv_web/live/member_live/index.html.heex:196
#: lib/mv_web/live/user_live/form.ex:141 #: lib/mv_web/live/user_live/form.ex:265
#: lib/mv_web/live/user_live/index.html.heex:59 #: lib/mv_web/live/user_live/index.html.heex:66
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Edit" msgid "Edit"
msgstr "" msgstr ""
@ -89,7 +89,7 @@ msgid "New Member"
msgstr "" msgstr ""
#: lib/mv_web/live/member_live/index.html.heex:193 #: lib/mv_web/live/member_live/index.html.heex:193
#: lib/mv_web/live/user_live/index.html.heex:56 #: lib/mv_web/live/user_live/index.html.heex:63
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Show" msgid "Show"
msgstr "" msgstr ""
@ -161,8 +161,9 @@ msgstr ""
#: lib/mv_web/live/custom_field_live/form.ex:66 #: lib/mv_web/live/custom_field_live/form.ex:66
#: lib/mv_web/live/custom_field_value_live/form.ex:74 #: lib/mv_web/live/custom_field_value_live/form.ex:74
#: lib/mv_web/live/global_settings_live.ex:55
#: lib/mv_web/live/member_live/form.ex:79 #: lib/mv_web/live/member_live/form.ex:79
#: lib/mv_web/live/user_live/form.ex:234 #: lib/mv_web/live/user_live/form.ex:248
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Saving..." msgid "Saving..."
msgstr "" msgstr ""
@ -184,7 +185,7 @@ msgstr ""
msgid "Id" msgid "Id"
msgstr "" msgstr ""
#: lib/mv_web/live/member_live/index/formatter.ex:65 #: lib/mv_web/live/member_live/index/formatter.ex:61
#: lib/mv_web/live/member_live/show.ex:53 #: lib/mv_web/live/member_live/show.ex:53
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "No" msgid "No"
@ -200,7 +201,7 @@ msgstr ""
msgid "This is a member record from your database." msgid "This is a member record from your database."
msgstr "" msgstr ""
#: lib/mv_web/live/member_live/index/formatter.ex:64 #: lib/mv_web/live/member_live/index/formatter.ex:60
#: lib/mv_web/live/member_live/show.ex:53 #: lib/mv_web/live/member_live/show.ex:53
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Yes" msgid "Yes"
@ -259,7 +260,7 @@ msgstr ""
#: lib/mv_web/live/custom_field_live/index.ex:120 #: lib/mv_web/live/custom_field_live/index.ex:120
#: lib/mv_web/live/custom_field_value_live/form.ex:77 #: lib/mv_web/live/custom_field_value_live/form.ex:77
#: lib/mv_web/live/member_live/form.ex:82 #: lib/mv_web/live/member_live/form.ex:82
#: lib/mv_web/live/user_live/form.ex:237 #: lib/mv_web/live/user_live/form.ex:251
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Cancel" msgid "Cancel"
msgstr "" msgstr ""
@ -294,7 +295,7 @@ msgstr ""
msgid "Immutable" msgid "Immutable"
msgstr "" msgstr ""
#: lib/mv_web/components/layouts/navbar.ex:94 #: lib/mv_web/components/layouts/navbar.ex:102
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Logout" msgid "Logout"
msgstr "" msgstr ""
@ -310,7 +311,7 @@ msgstr ""
msgid "Member" msgid "Member"
msgstr "" msgstr ""
#: lib/mv_web/components/layouts/navbar.ex:19 #: lib/mv_web/components/layouts/navbar.ex:25
#: lib/mv_web/live/member_live/index.ex:57 #: lib/mv_web/live/member_live/index.ex:57
#: 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
@ -339,7 +340,7 @@ msgstr ""
#: lib/mv_web/live/user_live/form.ex:107 #: lib/mv_web/live/user_live/form.ex:107
#: lib/mv_web/live/user_live/form.ex:115 #: lib/mv_web/live/user_live/form.ex:115
#: lib/mv_web/live/user_live/form.ex:210 #: lib/mv_web/live/user_live/form.ex:224
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Note" msgid "Note"
msgstr "" msgstr ""
@ -355,7 +356,7 @@ msgstr ""
msgid "Password Authentication" msgid "Password Authentication"
msgstr "" msgstr ""
#: lib/mv_web/components/layouts/navbar.ex:89 #: lib/mv_web/components/layouts/navbar.ex:95
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Profil" msgid "Profil"
msgstr "" msgstr ""
@ -375,12 +376,12 @@ msgstr ""
msgid "Select member" msgid "Select member"
msgstr "" msgstr ""
#: lib/mv_web/components/layouts/navbar.ex:92 #: lib/mv_web/components/layouts/navbar.ex:99
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Settings" msgid "Settings"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:235 #: lib/mv_web/live/user_live/form.ex:249
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Save User" msgid "Save User"
msgstr "" msgstr ""
@ -405,7 +406,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:252 #: lib/mv_web/live/user_live/form.ex:266
#: lib/mv_web/live/user_live/show.ex:34 #: lib/mv_web/live/user_live/show.ex:34
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "User" msgid "User"
@ -433,7 +434,7 @@ msgstr ""
msgid "descending" msgid "descending"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:251 #: lib/mv_web/live/user_live/form.ex:265
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "New" msgid "New"
msgstr "" msgstr ""
@ -543,14 +544,14 @@ msgstr ""
msgid "Back to users list" msgid "Back to users list"
msgstr "" msgstr ""
#: lib/mv_web/components/layouts/navbar.ex:27
#: lib/mv_web/components/layouts/navbar.ex:33 #: lib/mv_web/components/layouts/navbar.ex:33
#: lib/mv_web/components/layouts/navbar.ex:39
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Select language" msgid "Select language"
msgstr "" msgstr ""
#: lib/mv_web/components/layouts/navbar.ex:40 #: lib/mv_web/components/layouts/navbar.ex:46
#: lib/mv_web/components/layouts/navbar.ex:60 #: lib/mv_web/components/layouts/navbar.ex:66
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Toggle dark mode" msgid "Toggle dark mode"
msgstr "" msgstr ""
@ -561,7 +562,7 @@ msgstr ""
msgid "Search..." msgid "Search..."
msgstr "" msgstr ""
#: lib/mv_web/components/layouts/navbar.ex:21 #: lib/mv_web/components/layouts/navbar.ex:27
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Users" msgid "Users"
msgstr "" msgstr ""
@ -654,7 +655,7 @@ msgstr ""
msgid "Use this form to manage custom_field records in your database." msgid "Use this form to manage custom_field records in your database."
msgstr "" msgstr ""
#: lib/mv_web/components/layouts/navbar.ex:20 #: lib/mv_web/components/layouts/navbar.ex:26
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Custom Fields" msgid "Custom Fields"
msgstr "" msgstr ""
@ -701,37 +702,73 @@ msgstr ""
msgid "To confirm deletion, please enter this text:" msgid "To confirm deletion, please enter this text:"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:210 #: lib/mv_web/live/custom_field_live/form.ex:64
#, elixir-autogen, elixir-format
msgid "Show in overview"
msgstr ""
#: lib/mv_web/live/global_settings_live.ex:51
#, elixir-autogen, elixir-format
msgid "Association Name"
msgstr ""
#: lib/mv_web/live/global_settings_live.ex:31
#: lib/mv_web/live/global_settings_live.ex:41
#, elixir-autogen, elixir-format, fuzzy
msgid "Club Settings"
msgstr ""
#: lib/mv_web/live/global_settings_live.ex:43
#, elixir-autogen, elixir-format
msgid "Manage global settings for the association."
msgstr ""
#: lib/mv_web/live/global_settings_live.ex:56
#, elixir-autogen, elixir-format, fuzzy
msgid "Save Settings"
msgstr ""
#: lib/mv_web/live/global_settings_live.ex:75
#, elixir-autogen, elixir-format
msgid "Settings updated successfully"
msgstr ""
#: lib/mv_web/live/user_live/form.ex:224
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "A member with this email already exists. To link with a different member, please change one of the email addresses first." msgid "A member with this email already exists. To link with a different member, please change one of the email addresses first."
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:185 #: lib/mv_web/live/user_live/form.ex:192
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Available members" msgid "Available members"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:357
#, elixir-autogen, elixir-format
msgid "Failed to link member: %{error}"
msgstr ""
#: lib/mv_web/live/user_live/form.ex:152 #: lib/mv_web/live/user_live/form.ex:152
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Member will be unlinked when you save. Cannot select new member until saved." msgid "Member will be unlinked when you save. Cannot select new member until saved."
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:226 #: lib/mv_web/live/user_live/form.ex:240
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Save to confirm linking." msgid "Save to confirm linking."
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:169 #: lib/mv_web/live/user_live/form.ex:171
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Search for a member to link..." msgid "Search for a member to link..."
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:173 #: lib/mv_web/live/user_live/form.ex:175
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Search for member to link" msgid "Search for member to link"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:223 #: lib/mv_web/live/user_live/form.ex:237
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Selected" msgid "Selected"
msgstr "" msgstr ""
@ -746,15 +783,6 @@ msgstr ""
msgid "Unlinking scheduled" msgid "Unlinking scheduled"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:342
#, elixir-autogen, elixir-format
msgid "Failed to link member: %{error}"
msgstr ""
#: lib/mv_web/live/custom_field_live/form.ex:64
#, elixir-autogen, elixir-format
msgid "Show in overview"
msgstr ""
#~ #: lib/mv_web/live/custom_field_live/index.ex:97 #~ #: lib/mv_web/live/custom_field_live/index.ex:97
#~ #, elixir-autogen, elixir-format #~ #, elixir-autogen, elixir-format
#~ msgid "To confirm deletion, please enter the custom field slug:" #~ msgid "To confirm deletion, please enter the custom field slug:"

View file

@ -0,0 +1,31 @@
defmodule Mv.Repo.Migrations.AddSettingsTable 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 table(:settings, primary_key: false) do
add :id, :uuid, null: false, default: fragment("gen_random_uuid()"), primary_key: true
add :club_name, :text, null: false
add :inserted_at, :utc_datetime_usec,
null: false,
default: fragment("(now() AT TIME ZONE 'utc')")
add :updated_at, :utc_datetime_usec,
null: false,
default: fragment("(now() AT TIME ZONE 'utc')")
end
# Note: Singleton pattern is enforced at application level via get_settings/0
# which creates the record if it doesn't exist and only allows updates
end
def down do
drop table(:settings)
end
end

View file

@ -323,8 +323,21 @@ if friedrich = find_member.("friedrich.wagner@example.de") do
end) end)
end end
# Create or update global settings (singleton)
default_club_name = System.get_env("ASSOCIATION_NAME") || "Club Name"
case Membership.get_settings() do
{:ok, existing_settings} ->
# Settings exist, update if club_name is different from env var
if existing_settings.club_name != default_club_name do
{:ok, _updated} =
Membership.update_settings(existing_settings, %{club_name: default_club_name})
end
end
IO.puts("✅ Seeds completed successfully!") IO.puts("✅ Seeds completed successfully!")
IO.puts("📝 Created sample data:") IO.puts("📝 Created sample data:")
IO.puts(" - Global settings: club_name = #{default_club_name}")
IO.puts(" - Custom fields: 12 fields (String, Date, Boolean, Email, + 8 realistic fields)") IO.puts(" - Custom fields: 12 fields (String, Date, Boolean, Email, + 8 realistic fields)")
IO.puts(" - Admin user: admin@mv.local (password: testpassword)") IO.puts(" - Admin user: admin@mv.local (password: testpassword)")
IO.puts(" - Sample members: Hans, Greta, Friedrich") IO.puts(" - Sample members: Hans, Greta, Friedrich")

View file

@ -0,0 +1,67 @@
{
"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": "club_name",
"type": "text"
},
{
"allow_nil?": false,
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
"generated?": false,
"precision": null,
"primary_key?": false,
"references": null,
"scale": null,
"size": null,
"source": "inserted_at",
"type": "utc_datetime_usec"
},
{
"allow_nil?": false,
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
"generated?": false,
"precision": null,
"primary_key?": false,
"references": null,
"scale": null,
"size": null,
"source": "updated_at",
"type": "utc_datetime_usec"
}
],
"base_filter": null,
"check_constraints": [],
"custom_indexes": [],
"custom_statements": [],
"has_create_action": true,
"hash": "353EB39F18B97C596A77A78A060FB9DE075AAD731F74F64AB62D357CBCDEC914",
"identities": [],
"multitenancy": {
"attribute": null,
"global": null,
"strategy": null
},
"repo": "Elixir.Mv.Repo",
"schema": null,
"table": "settings"
}

View file

@ -0,0 +1,61 @@
defmodule Mv.Membership.SettingEnvTest do
use Mv.DataCase, async: false
alias Mv.Membership
describe "Settings with environment variable" do
test "club_name can be set via ASSOCIATION_NAME environment variable" do
# Set environment variable
System.put_env("ASSOCIATION_NAME", "Test Association from Env")
try do
# Get settings - should use environment variable if no DB value exists
{:ok, settings} = Membership.get_settings()
# If settings don't have a club_name in DB, it should use the env var
# This depends on implementation - we'll check that the env var is respected
assert settings.club_name != nil
after
# Clean up
System.delete_env("ASSOCIATION_NAME")
end
end
test "database value takes precedence over environment variable" do
# Set environment variable
System.put_env("ASSOCIATION_NAME", "Env Value")
try do
# Set a value in the database
{:ok, settings} = Membership.get_settings()
{:ok, _updated} = Membership.update_settings(settings, %{club_name: "DB Value"})
# Get settings again - should use DB value, not env var
{:ok, settings_after} = Membership.get_settings()
assert settings_after.club_name == "DB Value"
after
# Clean up
System.delete_env("ASSOCIATION_NAME")
end
end
test "uses environment variable when database value is not set" do
# Set environment variable
System.put_env("ASSOCIATION_NAME", "Default from Env")
try do
# Clear database value (if possible) or check that env var is used
{:ok, settings} = Membership.get_settings()
# If club_name is nil or empty in DB, should use env var
# This test depends on implementation details
# We're testing that the env var fallback works
club_name = settings.club_name || System.get_env("ASSOCIATION_NAME")
assert club_name != nil
assert club_name != ""
after
# Clean up
System.delete_env("ASSOCIATION_NAME")
end
end
end
end

View file

@ -0,0 +1,51 @@
defmodule Mv.Membership.SettingTest do
use Mv.DataCase, async: false
alias Mv.Membership
describe "Settings Resource" do
test "can read settings" do
# Settings should be a singleton resource
assert {:ok, _settings} = Membership.get_settings()
end
test "settings have club_name attribute" do
{:ok, settings} = Membership.get_settings()
assert Map.has_key?(settings, :club_name)
end
test "can update club_name" do
{:ok, settings} = Membership.get_settings()
assert {:ok, updated_settings} =
Membership.update_settings(settings, %{club_name: "New Club Name"})
assert updated_settings.club_name == "New Club Name"
end
test "club_name is required" do
{:ok, settings} = Membership.get_settings()
assert {:error, %Ash.Error.Invalid{errors: errors}} =
Membership.update_settings(settings, %{club_name: nil})
assert error_message(errors, :club_name) =~ "must be present"
end
test "club_name cannot be empty" do
{:ok, settings} = Membership.get_settings()
assert {:error, %Ash.Error.Invalid{errors: errors}} =
Membership.update_settings(settings, %{club_name: ""})
assert error_message(errors, :club_name) =~ "must be present"
end
end
# Helper function to extract error messages
defp error_message(errors, field) do
errors
|> Enum.filter(fn err -> Map.get(err, :field) == field end)
|> Enum.map(&Map.get(&1, :message, ""))
|> List.first() || ""
end
end

View file

@ -84,5 +84,23 @@ defmodule MvWeb.Layouts.NavbarTest do
# Check for correct logout path # Check for correct logout path
assert html =~ ~s(href="/sign-out") assert html =~ ~s(href="/sign-out")
end end
test "Settings link navigates to global settings page", %{conn: conn} do
user = create_test_user(%{email: "test@example.com"})
conn = conn_with_oidc_user(conn, user)
html =
render_component(&MvWeb.Layouts.Navbar.navbar/1, %{
current_user: user
})
# Check that Settings link exists and points to /settings
assert html =~ "Settings"
assert html =~ ~s(href="/settings") || html =~ ~s(navigate="/settings")
# Verify the link actually works by navigating to it
{:ok, _view, settings_html} = live(conn, ~p"/settings")
assert settings_html =~ "Club Settings"
end
end end
end end

View file

@ -1,10 +1,11 @@
defmodule MvWeb.PageControllerTest do defmodule MvWeb.PageControllerTest do
use MvWeb.ConnCase use MvWeb.ConnCase, async: true
test "GET /", %{conn: conn} do test "renders home template successfully with authenticated user", %{conn: conn} do
conn = conn_with_oidc_user(conn) user = create_test_user(%{email: "test@example.com"})
conn = conn_with_oidc_user(conn, user)
conn = get(conn, "/")
conn = get(conn, ~p"/") assert html_response(conn, 200)
assert html_response(conn, 200) =~ "Mitgliederverwaltung"
end end
end end

View file

@ -0,0 +1,68 @@
defmodule MvWeb.GlobalSettingsLiveTest do
use MvWeb.ConnCase, async: true
import Phoenix.LiveViewTest
alias Mv.Membership
describe "Global Settings LiveView" do
setup %{conn: conn} do
user = create_test_user(%{email: "admin@example.com"})
conn = conn_with_oidc_user(conn, user)
{:ok, conn: conn, user: user}
end
test "renders the global settings page", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/settings")
assert html =~ "Club Settings"
assert html =~ "Settings"
end
test "displays current club name", %{conn: conn} do
# Set initial club name
{:ok, settings} = Membership.get_settings()
{:ok, _updated} = Membership.update_settings(settings, %{club_name: "Test Club"})
{:ok, _view, html} = live(conn, ~p"/settings")
assert html =~ "Test Club"
end
test "can update club name via form", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/settings")
# Submit form with new club name
assert view
|> form("#settings-form", %{setting: %{club_name: "Updated Club Name"}})
|> render_submit()
# Check for success message
assert render(view) =~ "Settings updated successfully"
assert render(view) =~ "Updated Club Name"
end
test "shows error when club_name is empty", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/settings")
# Submit form with empty club name
html =
view
|> form("#settings-form", %{setting: %{club_name: ""}})
|> render_submit()
assert html =~ "must be present"
end
test "shows error when club_name is missing", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/settings")
# Submit form with club_name explicitly set to empty string
# (Phoenix forms will keep existing value if field is omitted)
html =
view
|> form("#settings-form", %{setting: %{club_name: ""}})
|> render_submit()
assert html =~ "must be present"
end
end
end