Merge branch 'main' into feature/209_hide_field_dropdown
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
carla 2025-12-03 12:52:12 +01:00
commit f0613fe1e5
29 changed files with 1661 additions and 405 deletions

View file

@ -24,7 +24,7 @@ defmodule Mv.Membership.Member do
- Email format validation (using EctoCommons.EmailValidator)
- Phone number format: international format with 6-20 digits
- Postal code format: exactly 5 digits (German format)
- Date validations: birth_date and join_date not in future, exit_date after join_date
- Date validations: join_date not in future, exit_date after join_date
- Email uniqueness: prevents conflicts with unlinked users
## Full-Text Search
@ -284,11 +284,6 @@ defmodule Mv.Membership.Member do
end
end
# Birth date not in the future
validate compare(:birth_date, less_than_or_equal_to: &Date.utc_today/0),
where: [present(:birth_date)],
message: "cannot be in the future"
# Join date not in the future
validate compare(:join_date, less_than_or_equal_to: &Date.utc_today/0),
where: [present(:join_date)],
@ -351,10 +346,6 @@ defmodule Mv.Membership.Member do
constraints min_length: 5, max_length: 254
end
attribute :birth_date, :date do
allow_nil? true
end
attribute :paid, :boolean do
allow_nil? true
end

View file

@ -134,8 +134,8 @@ defmodule Mv.Membership do
## Parameters
- `settings` - The settings record to update
- `visibility_config` - A map of member field names (atoms) to boolean visibility values
(e.g., `%{street: false, house_number: false}`)
- `visibility_config` - A map of member field names (strings) to boolean visibility values
(e.g., `%{"street" => false, "house_number" => false}`)
## Returns
@ -145,9 +145,9 @@ defmodule Mv.Membership do
## Examples
iex> {:ok, settings} = Mv.Membership.get_settings()
iex> {:ok, updated} = Mv.Membership.update_member_field_visibility(settings, %{street: false, house_number: false})
iex> {:ok, updated} = Mv.Membership.update_member_field_visibility(settings, %{"street" => false, "house_number" => false})
iex> updated.member_field_visibility
%{street: false, house_number: false}
%{"street" => false, "house_number" => false}
"""
def update_member_field_visibility(settings, visibility_config) do

View file

@ -10,7 +10,7 @@ defmodule Mv.Membership.Setting do
## Attributes
- `club_name` - The name of the association/club (required, cannot be empty)
- `member_field_visibility` - JSONB map storing visibility configuration for member fields
(e.g., `%{street: false, house_number: false}`). Fields not in the map default to `true`.
(e.g., `%{"street" => false, "house_number" => false}`). Fields not in the map default to `true`.
## Singleton Pattern
This resource uses a singleton pattern - there should only be one settings record.
@ -32,7 +32,7 @@ defmodule Mv.Membership.Setting do
{:ok, updated} = Mv.Membership.update_settings(settings, %{club_name: "New Name"})
# Update member field visibility
{:ok, updated} = Mv.Membership.update_member_field_visibility(settings, %{street: false, house_number: false})
{:ok, updated} = Mv.Membership.update_member_field_visibility(settings, %{"street" => false, "house_number" => false})
"""
use Ash.Resource,
domain: Mv.Membership,
@ -67,43 +67,6 @@ defmodule Mv.Membership.Setting do
description "Updates the visibility configuration for member fields in the overview"
require_atomic? false
accept [:member_field_visibility]
change fn changeset, _context ->
visibility = Ash.Changeset.get_attribute(changeset, :member_field_visibility)
if visibility && is_map(visibility) do
valid_fields = Mv.Constants.member_fields()
# Normalize keys to atoms (JSONB may return string keys)
invalid_keys =
Enum.filter(visibility, fn {key, _value} ->
atom_key =
if is_atom(key) do
key
else
try do
String.to_existing_atom(key)
rescue
ArgumentError -> nil
end
end
atom_key && atom_key not in valid_fields
end)
|> Enum.map(fn {key, _value} -> key end)
if Enum.empty?(invalid_keys) do
changeset
else
Ash.Changeset.add_error(
changeset,
field: :member_field_visibility,
message: "Invalid member field keys: #{inspect(invalid_keys)}"
)
end
else
changeset
end
end
end
end
@ -111,23 +74,39 @@ defmodule Mv.Membership.Setting do
validate present(:club_name), on: [:create, :update]
validate string_length(:club_name, min: 1), on: [:create, :update]
# Validate that member_field_visibility map contains only boolean values
# This allows dynamic fields without hardcoding specific field names
# Validate member_field_visibility map structure and content
validate fn changeset, _context ->
visibility = Ash.Changeset.get_attribute(changeset, :member_field_visibility)
if visibility && is_map(visibility) do
invalid_entries =
# Validate all values are booleans
invalid_values =
Enum.filter(visibility, fn {_key, value} ->
not is_boolean(value)
end)
if Enum.empty?(invalid_entries) do
:ok
else
{:error,
field: :member_field_visibility,
message: "All values in member_field_visibility must be booleans"}
# Validate all keys are valid member fields
valid_field_strings = Mv.Constants.member_fields() |> Enum.map(&Atom.to_string/1)
invalid_keys =
Enum.filter(visibility, fn {key, _value} ->
key not in valid_field_strings
end)
|> Enum.map(fn {key, _value} -> key end)
cond do
not Enum.empty?(invalid_values) ->
{:error,
field: :member_field_visibility,
message: "All values in member_field_visibility must be booleans"}
not Enum.empty?(invalid_keys) ->
{:error,
field: :member_field_visibility,
message: "Invalid member field keys: #{inspect(invalid_keys)}"}
true ->
:ok
end
else
:ok