All checks were successful
continuous-integration/drone/push Build is passing
138 lines
4.6 KiB
Elixir
138 lines
4.6 KiB
Elixir
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)
|
|
- `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.
|
|
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"})
|
|
|
|
# Update member field visibility
|
|
{:ok, updated} = Mv.Membership.update_member_field_visibility(settings, %{"street" => false, "house_number" => false})
|
|
"""
|
|
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, :member_field_visibility]
|
|
end
|
|
|
|
update :update do
|
|
primary? true
|
|
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]
|
|
end
|
|
end
|
|
|
|
validations do
|
|
validate present(:club_name), on: [:create, :update]
|
|
validate string_length(:club_name, min: 1), on: [:create, :update]
|
|
|
|
# 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
|
|
# Validate all values are booleans
|
|
invalid_values =
|
|
Enum.filter(visibility, fn {_key, value} ->
|
|
not is_boolean(value)
|
|
end)
|
|
|
|
# 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
|
|
end
|
|
end,
|
|
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
|
|
]
|
|
|
|
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
|