150 lines
4.3 KiB
Elixir
150 lines
4.3 KiB
Elixir
defmodule Mv.Membership.CustomField do
|
|
@moduledoc """
|
|
Ash resource defining the schema for custom member fields.
|
|
|
|
## Overview
|
|
CustomFields define the "schema" for custom fields in the membership system.
|
|
Each CustomField specifies the name, data type, and behavior of a custom field
|
|
that can be attached to members via CustomFieldValue resources.
|
|
|
|
## Attributes
|
|
- `name` - Unique identifier for the custom field (e.g., "phone_mobile", "birthday")
|
|
- `slug` - URL-friendly, immutable identifier automatically generated from name (e.g., "phone-mobile")
|
|
- `value_type` - Data type constraint (`:string`, `:integer`, `:boolean`, `:date`, `:email`)
|
|
- `description` - Optional human-readable description
|
|
- `immutable` - If true, custom field values cannot be changed after creation
|
|
- `required` - If true, all members must have this custom field (future feature)
|
|
- `show_in_overview` - If true, this custom field will be displayed in the member overview table and can be sorted
|
|
|
|
## Supported Value Types
|
|
- `:string` - Text data (max 10,000 characters)
|
|
- `:integer` - Numeric data (64-bit integers)
|
|
- `:boolean` - True/false flags
|
|
- `:date` - Date values (no time component)
|
|
- `:email` - Validated email addresses (max 254 characters)
|
|
|
|
## Relationships
|
|
- `has_many :custom_field_values` - All custom field values of this type
|
|
|
|
## Constraints
|
|
- Name must be unique across all custom fields
|
|
- Name maximum length: 100 characters
|
|
- Deleting a custom field will cascade delete all associated custom field values
|
|
|
|
## Calculations
|
|
- `assigned_members_count` - Returns the number of distinct members with values for this custom field
|
|
|
|
## Examples
|
|
# Create a new custom field
|
|
CustomField.create!(%{
|
|
name: "phone_mobile",
|
|
value_type: :string,
|
|
description: "Mobile phone number"
|
|
})
|
|
|
|
# Create a required custom field
|
|
CustomField.create!(%{
|
|
name: "emergency_contact",
|
|
value_type: :string,
|
|
required: true
|
|
})
|
|
"""
|
|
use Ash.Resource,
|
|
domain: Mv.Membership,
|
|
data_layer: AshPostgres.DataLayer
|
|
|
|
postgres do
|
|
table "custom_fields"
|
|
repo Mv.Repo
|
|
end
|
|
|
|
actions do
|
|
defaults [:read, :update]
|
|
default_accept [:name, :value_type, :description, :immutable, :required, :show_in_overview]
|
|
|
|
create :create do
|
|
accept [:name, :value_type, :description, :immutable, :required, :show_in_overview]
|
|
change Mv.Membership.CustomField.Changes.GenerateSlug
|
|
validate string_length(:slug, min: 1)
|
|
end
|
|
|
|
destroy :destroy_with_values do
|
|
primary? true
|
|
end
|
|
|
|
read :prepare_deletion do
|
|
argument :id, :uuid, allow_nil?: false
|
|
|
|
filter expr(id == ^arg(:id))
|
|
prepare build(load: [:assigned_members_count])
|
|
end
|
|
end
|
|
|
|
attributes do
|
|
uuid_primary_key :id
|
|
|
|
attribute :name, :string,
|
|
allow_nil?: false,
|
|
public?: true,
|
|
constraints: [
|
|
max_length: 100,
|
|
trim?: true
|
|
]
|
|
|
|
attribute :slug, :string,
|
|
allow_nil?: false,
|
|
public?: true,
|
|
writable?: false,
|
|
constraints: [
|
|
max_length: 100,
|
|
trim?: true
|
|
]
|
|
|
|
attribute :value_type, :atom,
|
|
constraints: [one_of: [:string, :integer, :boolean, :date, :email]],
|
|
allow_nil?: false,
|
|
description: "Defines the datatype `CustomFieldValue.value` is interpreted as"
|
|
|
|
attribute :description, :string,
|
|
allow_nil?: true,
|
|
public?: true,
|
|
constraints: [
|
|
max_length: 500,
|
|
trim?: true
|
|
]
|
|
|
|
attribute :immutable, :boolean,
|
|
default: false,
|
|
allow_nil?: false
|
|
|
|
attribute :required, :boolean,
|
|
default: false,
|
|
allow_nil?: false
|
|
|
|
attribute :show_in_overview, :boolean,
|
|
default: true,
|
|
allow_nil?: false,
|
|
public?: true,
|
|
description: "If true, this custom field will be displayed in the member overview table"
|
|
end
|
|
|
|
relationships do
|
|
has_many :custom_field_values, Mv.Membership.CustomFieldValue
|
|
end
|
|
|
|
calculations do
|
|
calculate :assigned_members_count,
|
|
:integer,
|
|
expr(
|
|
fragment(
|
|
"(SELECT COUNT(DISTINCT member_id) FROM custom_field_values WHERE custom_field_id = ?)",
|
|
id
|
|
)
|
|
)
|
|
end
|
|
|
|
identities do
|
|
identity :unique_name, [:name]
|
|
identity :unique_slug, [:slug]
|
|
end
|
|
end
|