# `PhoenixKit.Users.CustomFields`
[🔗](https://github.com/BeamLabEU/phoenix_kit/blob/v1.7.106/lib/phoenix_kit/users/custom_fields.ex#L1)

Context for managing custom user field definitions.

This module provides functionality to define, manage, and validate custom fields
that can be added to user profiles. Field definitions are stored as JSON in the
settings table, and actual field values are stored in the user's `custom_fields`
JSONB column.

## Field Definition Structure

Each field definition is a map with the following keys:

- `key` - Unique identifier for the field (string)
- `label` - Display label for the field (string)
- `type` - Field type: text, textarea, number, boolean, date, email, url, uuid, select, radio, checkbox
- `required` - Whether the field is required (boolean)
- `position` - Display order (integer)
- `enabled` - Whether the field is active (boolean)
- `user_accessible` - Whether users can edit this field from their settings page (boolean, default: true)
- `validation` - Optional validation rules (map)
- `default` - Default value (string)
- `options` - For select/radio/checkbox types (list of strings)

## Examples

    # Get all field definitions
    CustomFields.list_field_definitions()

    # Add a new field
    CustomFields.add_field_definition(%{
      "key" => "phone",
      "label" => "Phone Number",
      "type" => "text",
      "required" => false,
      "position" => 1,
      "enabled" => true
    })

    # Get system configuration
    CustomFields.get_config()

# `add_field_definition`

Adds a new field definition.

Validates the field structure and ensures the key is unique.

## Examples

    iex> add_field_definition(%{"key" => "phone", "label" => "Phone", "type" => "text"})
    {:ok, _setting}

    iex> add_field_definition(%{"key" => "phone"})
    {:error, "Field with key 'phone' already exists"}

# `delete_field_definition`

Deletes a field definition by key.

## Examples

    iex> delete_field_definition("phone")
    {:ok, _setting}

# `ensure_definitions_exist`

Ensures field definitions exist for all keys in the given custom fields map.

For any key that doesn't have a corresponding field definition, auto-creates one
with a label derived from the key, type inferred from the value, and
`user_accessible: false` (admin-only by default).

Returns `:ok`. Logs warnings for any definitions that fail to create.

# `get_config`

Gets the current custom fields system configuration.

Returns a map with field counts.

## Examples

    iex> get_config()
    %{
      field_count: 5,
      enabled_field_count: 3
    }

# `get_field_definition`

Gets a single field definition by key.

Returns `nil` if not found.

## Examples

    iex> get_field_definition("phone")
    %{"key" => "phone", "label" => "Phone Number", ...}

    iex> get_field_definition("nonexistent")
    nil

# `get_option_text`

Gets the display text for a select field value (which is stored as index).

For select/radio/checkbox fields, the saved value is the index (0, 1, 2...)
and this function returns the actual option text.

## Examples

    iex> get_option_text("favorite_color", "1")
    "Blue"  # if options are ["Red", "Blue", "Green"]

    iex> get_option_text("nonexistent", "1")
    "1"  # fallback to raw value

# `infer_field_type`

Infers the custom field type from a value.

Returns one of: `"boolean"`, `"number"`, `"uuid"`, `"url"`, `"email"`, `"text"`.

# `list_enabled_field_definitions`

Returns only enabled field definitions, sorted by position.

## Examples

    iex> list_enabled_field_definitions()
    [%{"key" => "phone", "enabled" => true, ...}]

# `list_field_definitions`

Returns the list of all custom field definitions.

## Examples

    iex> list_field_definitions()
    [
      %{
        "key" => "phone",
        "label" => "Phone Number",
        "type" => "text",
        ...
      }
    ]

# `list_user_accessible_field_definitions`

Returns only enabled field definitions that are user-accessible, sorted by position.

These are fields that users can view and edit from their own settings page.
Admins can always see and edit all fields regardless of this setting.

Legacy fields without the `user_accessible` key default to `true` (accessible).

## Examples

    iex> list_user_accessible_field_definitions()
    [%{"key" => "phone", "enabled" => true, "user_accessible" => true, ...}]

# `reorder_field_definitions`

Reorders field definitions by updating their position values.

Accepts a list of keys in the desired order.

## Examples

    iex> reorder_field_definitions(["email", "phone", "department"])
    {:ok, _setting}

# `save_field_definitions`

Saves the complete list of field definitions.

## Examples

    iex> save_field_definitions([%{"key" => "phone", ...}])
    {:ok, _setting}

# `update_field_definition`

Updates an existing field definition.

## Examples

    iex> update_field_definition("phone", %{"label" => "Mobile Number"})
    {:ok, _setting}

    iex> update_field_definition("nonexistent", %{})
    {:error, "Field with key 'nonexistent' not found"}

# `validate_custom_field_value`

Validates a user's custom field value against a field definition.

## Examples

    iex> validate_custom_field_value(%{"type" => "email", "required" => true}, "test@example.com")
    :ok

    iex> validate_custom_field_value(%{"type" => "email", "required" => true}, nil)
    {:error, "Field is required"}

# `validate_field_definition`

Validates a field definition structure.

Checks for required keys, valid types, and proper structure.

## Examples

    iex> validate_field_definition(%{"key" => "phone", "type" => "text"})
    :ok

    iex> validate_field_definition(%{"key" => "phone", "type" => "invalid"})
    {:error, "Invalid field type: invalid"}

# `validate_user_custom_fields`

Validates all custom fields for a user against field definitions.

Returns `:ok` if valid, or `{:error, errors}` with a map of field keys to error messages.

## Examples

    iex> validate_user_custom_fields(%User{custom_fields: %{"phone" => "555-1234"}})
    :ok

    iex> validate_user_custom_fields(%User{custom_fields: %{"email" => "invalid"}})
    {:error, %{"email" => "Invalid email format"}}

---

*Consult [api-reference.md](api-reference.md) for complete listing*
