diff --git a/lib/membership/membership.ex b/lib/membership/membership.ex index c9d0466..cb3691b 100644 --- a/lib/membership/membership.ex +++ b/lib/membership/membership.ex @@ -62,7 +62,7 @@ defmodule Mv.Membership do 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 "Mitgliederverwaltung" as default. + `ASSOCIATION_NAME` environment variable or "Club Name" as default. ## Returns @@ -82,7 +82,7 @@ defmodule Mv.Membership do 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") || "Mitgliederverwaltung" + default_club_name = System.get_env("ASSOCIATION_NAME") || "Club Name" Mv.Membership.Setting |> Ash.Changeset.for_create(:create, %{club_name: default_club_name}) diff --git a/lib/membership/setting.ex b/lib/membership/setting.ex index 47b9dd8..38624dc 100644 --- a/lib/membership/setting.ex +++ b/lib/membership/setting.ex @@ -58,6 +58,11 @@ defmodule Mv.Membership.Setting do 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 @@ -72,9 +77,4 @@ defmodule Mv.Membership.Setting do timestamps() end - - validations do - validate present(:club_name), on: [:create, :update] - validate string_length(:club_name, min: 1), on: [:create, :update] - end end diff --git a/lib/mv_web/components/layouts/navbar.ex b/lib/mv_web/components/layouts/navbar.ex index 1de4c7f..7ff7f25 100644 --- a/lib/mv_web/components/layouts/navbar.ex +++ b/lib/mv_web/components/layouts/navbar.ex @@ -6,15 +6,21 @@ defmodule MvWeb.Layouts.Navbar do use Gettext, backend: MvWeb.Gettext use MvWeb, :verified_routes + alias Mv.Membership + attr :current_user, :map, required: true, doc: "The current user - navbar is only shown when user is present" def navbar(assigns) do + club_name = get_club_name() + + assigns = assign(assigns, :club_name, club_name) + ~H""" """ 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 diff --git a/lib/mv_web/live/global_settings_live.ex b/lib/mv_web/live/global_settings_live.ex new file mode 100644 index 0000000..0be4559 --- /dev/null +++ b/lib/mv_web/live/global_settings_live.ex @@ -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""" + + <.header> + {gettext("Club Settings")} + <:subtitle> + {gettext("Manage global settings for the association.")} + + + + <.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")} + + + + """ + 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 diff --git a/lib/mv_web/router.ex b/lib/mv_web/router.ex index d2a63bc..09a2792 100644 --- a/lib/mv_web/router.ex +++ b/lib/mv_web/router.ex @@ -73,6 +73,8 @@ defmodule MvWeb.Router do live "/users/:id", UserLive.Show, :show live "/users/:id/show/edit", UserLive.Show, :edit + live "/settings", GlobalSettingsLive + post "/set_locale", LocaleController, :set_locale end diff --git a/priv/repo/seeds.exs b/priv/repo/seeds.exs index 8d3cb6f..00cf657 100644 --- a/priv/repo/seeds.exs +++ b/priv/repo/seeds.exs @@ -323,8 +323,27 @@ if friedrich = find_member.("friedrich.wagner@example.de") do 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 + + {:ok, nil} -> + # Settings don't exist, create them + Mv.Membership.Setting + |> Ash.Changeset.for_create(:create, %{club_name: default_club_name}) + |> Ash.create!(domain: Mv.Membership) +end + IO.puts("✅ Seeds completed successfully!") 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(" - Admin user: admin@mv.local (password: testpassword)") IO.puts(" - Sample members: Hans, Greta, Friedrich")