feat: adds member visibility settings
This commit is contained in:
parent
831149f463
commit
397cbde9d6
3 changed files with 178 additions and 2 deletions
|
|
@ -434,6 +434,70 @@ defmodule Mv.Membership.Member do
|
|||
identity :unique_email, [:email]
|
||||
end
|
||||
|
||||
@doc """
|
||||
Checks if a member field should be shown in the overview.
|
||||
|
||||
Reads the visibility configuration from Settings resource. If a field is not
|
||||
configured in settings, it defaults to `true` (visible).
|
||||
|
||||
## Parameters
|
||||
- `field` - Atom representing the member field name (e.g., `:email`, `:street`)
|
||||
|
||||
## Returns
|
||||
- `true` if the field should be shown in overview (default)
|
||||
- `false` if the field is configured as hidden in settings
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Member.show_in_overview?(:email)
|
||||
true
|
||||
|
||||
iex> Member.show_in_overview?(:street)
|
||||
true # or false if configured in settings
|
||||
|
||||
"""
|
||||
@spec show_in_overview?(atom()) :: boolean()
|
||||
def show_in_overview?(field) when is_atom(field) do
|
||||
case Mv.Membership.get_settings() do
|
||||
{:ok, settings} ->
|
||||
visibility_config = settings.member_field_visibility || %{}
|
||||
# Normalize map keys to atoms (JSONB may return string keys)
|
||||
normalized_config = normalize_visibility_config(visibility_config)
|
||||
|
||||
# Get value from normalized config, default to true
|
||||
Map.get(normalized_config, field, true)
|
||||
|
||||
{:error, _} ->
|
||||
# If settings can't be loaded, default to visible
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
def show_in_overview?(_), do: true
|
||||
|
||||
# Normalizes visibility config map keys from strings to atoms.
|
||||
# JSONB in PostgreSQL converts atom keys to string keys when storing.
|
||||
defp normalize_visibility_config(config) when is_map(config) do
|
||||
Enum.reduce(config, %{}, fn
|
||||
{key, value}, acc when is_atom(key) ->
|
||||
Map.put(acc, key, value)
|
||||
|
||||
{key, value}, acc when is_binary(key) ->
|
||||
try do
|
||||
atom_key = String.to_existing_atom(key)
|
||||
Map.put(acc, atom_key, value)
|
||||
rescue
|
||||
ArgumentError ->
|
||||
acc
|
||||
end
|
||||
|
||||
_, acc ->
|
||||
acc
|
||||
end)
|
||||
end
|
||||
|
||||
defp normalize_visibility_config(_), do: %{}
|
||||
|
||||
@doc """
|
||||
Performs fuzzy search on members using PostgreSQL trigram similarity.
|
||||
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ defmodule Mv.Membership do
|
|||
# It's only used internally as fallback in get_settings/0
|
||||
# Settings should be created via seed script
|
||||
define :update_settings, action: :update
|
||||
define :update_member_field_visibility, action: :update_member_field_visibility
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -123,4 +124,37 @@ defmodule Mv.Membership do
|
|||
|> Ash.Changeset.for_update(:update, attrs)
|
||||
|> Ash.update(domain: __MODULE__)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Updates the member field visibility configuration.
|
||||
|
||||
This is a specialized action for updating only the member field visibility settings.
|
||||
It validates that all keys are valid member fields and all values are booleans.
|
||||
|
||||
## 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}`)
|
||||
|
||||
## 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_member_field_visibility(settings, %{street: false, house_number: false})
|
||||
iex> updated.member_field_visibility
|
||||
%{street: false, house_number: false}
|
||||
|
||||
"""
|
||||
def update_member_field_visibility(settings, visibility_config) do
|
||||
settings
|
||||
|> Ash.Changeset.for_update(:update_member_field_visibility, %{
|
||||
member_field_visibility: visibility_config
|
||||
})
|
||||
|> Ash.update(domain: __MODULE__)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ 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`.
|
||||
|
||||
## Singleton Pattern
|
||||
This resource uses a singleton pattern - there should only be one settings record.
|
||||
|
|
@ -28,6 +30,9 @@ defmodule Mv.Membership.Setting do
|
|||
|
||||
# Update club name
|
||||
{: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})
|
||||
"""
|
||||
use Ash.Resource,
|
||||
domain: Mv.Membership,
|
||||
|
|
@ -49,18 +54,86 @@ defmodule Mv.Membership.Setting do
|
|||
# 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]
|
||||
accept [:club_name, :member_field_visibility]
|
||||
end
|
||||
|
||||
update :update do
|
||||
primary? true
|
||||
accept [:club_name]
|
||||
require_atomic? false
|
||||
accept [:club_name, :member_field_visibility]
|
||||
end
|
||||
|
||||
update :update_member_field_visibility 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
|
||||
|
||||
validations 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 fn changeset, _context ->
|
||||
visibility = Ash.Changeset.get_attribute(changeset, :member_field_visibility)
|
||||
|
||||
if visibility && is_map(visibility) do
|
||||
invalid_entries =
|
||||
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"}
|
||||
end
|
||||
else
|
||||
:ok
|
||||
end
|
||||
end,
|
||||
on: [:create, :update]
|
||||
end
|
||||
|
||||
attributes do
|
||||
|
|
@ -75,6 +148,11 @@ defmodule Mv.Membership.Setting do
|
|||
min_length: 1
|
||||
]
|
||||
|
||||
attribute :member_field_visibility, :map,
|
||||
allow_nil?: true,
|
||||
public?: true,
|
||||
description: "Configuration for member field visibility in overview (JSONB map). Keys are member field names (atoms), values are booleans."
|
||||
|
||||
timestamps()
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue