Skip to content

AMSDAL FrontendConfig Plugin

The AMSDAL FrontendConfig plugin provides the ability to create configurations for the AMSDAL portal and other similar frontend applications. It builds configurations automatically for models and transactions, and you can add your custom configuration for any model to define how it should be displayed in the frontend.

To enable this plugin, add amsdal.contrib.frontend_configs.app.FrontendConfigAppConfig value to AMSDAL_CONTRIBS environment variable (enabled by default).

Frontend configs examples

Frontend configs are forms, that can be used to generate UI and interact with backend

Controls

Controls are main building blocks for the forms, it's elements. Some elements can contain other elements.

Text

It's the simplest element that has type=text and represents simple text input

{
  "type": "text",
  "id": "first_name_field",
  "name": "first_name",
  "label": "First Name"
}

Phone

Input with type=phone that adds frontend validation to the input

{
  "type": "phone",
  "id": "phone_field",
  "name": "mobile_phone",
  "label": "Mobile Phone",
  "helperText": "At least phone or email is required"
}

The helperText attribute is supported on any input control and renders a small hint below the field.

Email

Input with type=email that adds frontend validation to the input

{
  "type": "email",
  "id": "personal_email_field",
  "name": "personal_email",
  "label": "Personal Email"
}

Paragraph

Readonly element with type=paragraph that represents

tag

{
  "type": "paragraph",
  "label": "Info Text",
  "value": "This is the text in paragraph"
}
{
  "type": "paragraph",
  "label": "",
  "value": "This is the text in paragraph",
  "hideLabel": true
}

Infoscreen

Another way of displaying simple readonly text with type=infoscreen, but this time inside

element, and the main text should be in label instead of value

{
  "type": "infoscreen",
  "label": "The main text that will be displayed"
}

Readonly element with type=header that represents

tag

{
  "type": "header",
  "label": "Header Label",
  "value": "Header Text"
}
{
  "type": "header",
  "label": "",
  "value": "Header Text",
  "hideLabel": true
}

Select

Dropdown element with type=select. Requires an options array of {label, value} objects.

{
  "type": "select",
  "id": "state_field",
  "name": "state",
  "label": "State",
  "value": "CA",
  "options": [
    {"label": "California", "value": "CA"},
    {"label": "Texas", "value": "TX"}
  ]
}

Multiselect

Multi-selection dropdown with type=multiselect. Like select but allows multiple values to be chosen simultaneously. Value is an array of selected option values. Requires an options array of {label, value} objects.

{
  "type": "multiselect",
  "id": "riders_field",
  "name": "riders",
  "label": "Riders",
  "value": ["LTC", "WB"],
  "options": [
    {"label": "Long-Term Care", "value": "LTC"},
    {"label": "Waiver of Surrender", "value": "WB"},
    {"label": "Return of Premium", "value": "ROP"},
    {"label": "Enhanced Death Benefit", "value": "EDB"}
  ]
}

When sent to the backend the field value is a JSON array of the selected values:

{"riders": ["LTC", "WB"]}

Empty selection produces an empty array ([]), not null.

Password

Masked text input with type=password

{
  "type": "password",
  "id": "current_password_field",
  "name": "current_password",
  "label": "Current Password",
  "required": true,
  "value": ""
}

Date

Date picker with type=date. Value is ISO 8601 date string (YYYY-MM-DD).

{
  "type": "date",
  "id": "date_of_birth_field",
  "name": "date_of_birth",
  "label": "Date of Birth",
  "value": "1990-05-15"
}

Number

Numeric input with type=number

{
  "type": "number",
  "id": "premium_field",
  "name": "premium",
  "label": "Premium",
  "value": "100.00",
  "placeholder": "0.00",
  "required": true
}

Checkbox

Boolean toggle with type=checkbox. Value is the string "true" or "false" (not a JSON boolean).

{
  "type": "checkbox",
  "id": "us_citizen_field",
  "name": "us_citizen",
  "label": "U.S. Citizen?",
  "value": "false"
}

Textarea

Multi-line text input with type=textarea. Supports an optional rows attribute to control the visible height.

{
  "type": "textarea",
  "id": "notes_field",
  "name": "notes",
  "label": "Notes",
  "value": "",
  "placeholder": "Enter notes...",
  "rows": 4
}

Object Latest

Entity selector with type=object_latest. Lets the user pick an AMSDAL model instance by its latest version. The entityType must match the registered model name.

{
  "type": "object_latest",
  "id": "policy_field",
  "name": "policy",
  "label": "Policy",
  "entityType": "Policy",
  "value": "",
  "required": true
}

Button

Clickable element that fires one or more actions. Usually has hideLabel: true (the label becomes the button text rendered by the frontend). Supports an optional icon name and additional_styles for CSS overrides.

{
  "type": "button",
  "name": "save_button",
  "label": "Save",
  "hideLabel": true,
  "icon": "refresh",
  "additional_styles": {
    "width": "45%",
    "marginLeft": "27.5%",
    "borderRadius": "8px"
  },
  "actions": [
    {
      "type": "invoke",
      "method": "POST",
      "url": "/api/transactions/update_profile/",
      "body": {
        "first_name": "{first_name_field}"
      },
      "onSuccess": [],
      "onError": []
    }
  ]
}

Chat

Read-only chat thread display with type=chat. Value is an array of message objects.

{
  "type": "chat",
  "id": "chat_field_id",
  "name": "chat_field",
  "label": "Chat",
  "hideLabel": true,
  "value": [
    {
      "role": "user",
      "content": "Hello",
      "content_type": "text",
      "attachments": null,
      "created_at": "2025-01-01T12:00:00Z"
    },
    {
      "role": "assistant",
      "content": "Hi there!",
      "content_type": "text",
      "attachments": null,
      "created_at": "2025-01-01T12:01:00Z"
    }
  ]
}

Message object fields:

  • role"user" or "assistant"
  • content — message text
  • content_type"text" or "markdown"
  • attachments — array of attachment references or null
  • created_at — ISO 8601 datetime string

Attachment

File upload control with type=attachment. Used alongside a chat or send-message button. The uploaded file references are passed as a field value in invoke body.

{
  "type": "attachment",
  "id": "chat_attachments_field",
  "name": "chat_attachments",
  "label": "Attachments",
  "hideLabel": true
}

Container elements

Container elements group other controls. They do not submit values themselves.

Group

Top-level container returned by most transactions. Wraps all other controls.

{
  "type": "group",
  "name": "user_profile",
  "label": "User Profile",
  "controls": [...]
}

Sections

Renders child section controls as tabs or an accordion. Must contain only section children.

{
  "type": "sections",
  "name": "profile_sections",
  "label": "Profile Sections",
  "controls": [
    {"type": "section", "name": "personal_info", "label": "Personal Info", "controls": [...]}
  ]
}

Section

A named group with a visible label. Can appear standalone or inside sections. Supports an optional condition to control visibility.

{
  "type": "section",
  "name": "joint_owner_section",
  "label": "Joint Owner",
  "condition": {
    "path": "owner_type",
    "condition": "eq",
    "value": "Joint"
  },
  "controls": [...]
}

Common control attributes

Attribute Type Description
id string Unique field ID — used in {field_id} body references
type string Control type
name string Field name sent to the backend
label string Visible label text
value any Current / pre-filled value
required boolean Marks the field as mandatory
placeholder string Placeholder hint (text, number, textarea)
options array [{label, value}] for select and multiselect
controls array Child controls for group/section/sections
hideLabel boolean Suppress label rendering (common on buttons)
readOnly boolean Render field as read-only
condition object Visibility condition (see below)
on_blur array Actions fired when the field loses focus
actions array Actions fired on button click
icon string Icon identifier (e.g. "refresh", "send")
additional_styles object Inline CSS-like overrides
entityType string Model name for object_latest
helperText string Hint text rendered below the field
rows integer Visible row height for textarea

Conditions

A condition object attached to a control makes it visible only when the condition is true. It can be a simple single-field test or a compound logical expression.

Simple condition

{
  "target_id": "field_id",
  "condition": "eq",
  "value": "expected_value"
}
  • target_id — the id of another field in the same form
  • condition — comparison operator: "eq", "gte", "lte", "gt", "lt", "neq", "contains"
  • value — value to compare against (string, number, or boolean)

Example — show visa fields only when us_citizen is true (checkbox sends string "true"):

{
  "type": "text",
  "id": "country_of_citizenship_field",
  "name": "country_of_citizenship",
  "label": "Country of Citizenship",
  "value": "",
  "condition": {
    "target_id": "us_citizen_field",
    "condition": "eq",
    "value": true
  }
}

Example — show beneficiary block only when count is >= n:

{
  "type": "section",
  "name": "beneficiary_2_section",
  "label": "Beneficiary 2",
  "condition": {
    "target_id": "primary_number_of_beneficiaries_field",
    "condition": "gte",
    "value": "2"
  },
  "controls": [...]
}

Compound condition

Use "operation" with a "conditions" array to combine multiple tests. Leaf conditions reference a field by target_id (the field's id attribute).

Simple compound:

{
  "operation": "and",
  "conditions": [
    {"target_id": "owner_type_field", "condition": "eq", "value": "Joint"},
    {"target_id": "plan_type_field", "condition": "eq", "value": "Qualified"}
  ]
}

Nested compound — conditions can be nested arbitrarily deep:

{
  "operation": "or",
  "conditions": [
    {
      "target_id": "boolean_field",
      "condition": "eq",
      "value": true
    },
    {
      "operation": "and",
      "conditions": [
        {
          "target_id": "text_field",
          "condition": "eq",
          "value": "No"
        },
        {
          "target_id": "select_field",
          "condition": "neq",
          "value": "No"
        }
      ]
    }
  ]
}

Supported operations: "and", "or".


Actions

Actions are objects that describe what happens when a button is clicked or an event fires (e.g. on_blur). A control can have multiple actions that execute in sequence.

invoke

Calls a backend transaction or REST endpoint.

{
  "type": "invoke",
  "method": "POST",
  "url": "/api/transactions/update_profile/",
  "body": {
    "first_name": "{first_name_field}",
    "state": "{state_field}"
  },
  "onSuccess": [],
  "onError": []
}
  • method — HTTP verb: GET, POST, PUT, PATCH, DELETE
  • url — API endpoint path
  • body — key/value map; field values are referenced as {field_id}
  • onSuccess — array of actions run after a successful response
  • onError — array of actions run on error
  • async — boolean; if true, the request is fire-and-forget (UI does not wait for response). Useful for autosave.

Example with async fire-and-forget:

{
  "type": "invoke",
  "method": "POST",
  "url": "/api/transactions/save_customer_application/",
  "async": true,
  "body": {
    "first_name": "{first_name_field}"
  }
}

update_form

Replaces the current form with fresh content fetched from a transaction.

{
  "type": "update_form",
  "control_source": {
    "type": "dynamic",
    "dynamic_value": {
      "type": "transaction",
      "entity_name": "get_security"
    }
  }
}

entity_name is the name of the transaction function registered with AMSDAL.

The dynamic_value also supports an optional body object to pass parameters to the target transaction. Values can reference row data using {row.field_name} syntax (available in on_row_click context):

{
  "type": "update_form",
  "control_source": {
    "type": "dynamic",
    "dynamic_value": {
      "type": "transaction",
      "entity_name": "get_customer_application_form",
      "body": {
        "application_id": "{row.partition_key}",
        "force_readonly": "true"
      }
    }
  }
}

show_popup

Opens a modal dialog containing its own set of controls.

{
  "type": "show_popup",
  "id": "save_success_popup",
  "controls": [
    {
      "type": "text",
      "value": "Your application has been saved successfully."
    },
    {
      "type": "button",
      "name": "close_popup_button",
      "label": "OK",
      "actions": [
        {"type": "close_popup", "target": "save_success_popup"}
      ]
    }
  ]
}

close_popup

Closes an open popup identified by its id.

{
  "type": "close_popup",
  "target": "save_success_popup"
}

Scrolls the page to a named dashboard section.

{
  "type": "navigate_to_section",
  "section_id": "profile"
}

Known section IDs: "profile", "applications", "policies", "secure_messaging".

open_url

Opens a URL (typically in a new tab).

{
  "type": "open_url",
  "url": "https://example.com/rates?state=CA"
}

update_value

Updates a field's displayed value, typically used in onSuccess to inject data from an API response.

{
  "type": "update_value",
  "field_id": "totp_secret_field",
  "value": "{response.details.secret}"
}

value can contain a {response.path.to.field} placeholder that is resolved from the previous invoke response.


additional_styles

additional_styles is an object of camelCase CSS property names applied as inline styles to the control's root element. It is supported on most controls: buttons, inputs, sections, and containers.

Common use-cases:

Centering a button with fixed width:

{
  "type": "button",
  "name": "submit_button",
  "label": "Submit Application",
  "hideLabel": true,
  "additional_styles": {
    "width": "50%",
    "marginLeft": "25%",
    "borderRadius": "8px"
  },
  "actions": [...]
}

Side-by-side buttons using flex layout on their parent section:

{
  "type": "section",
  "name": "action_buttons",
  "label": "",
  "hideLabel": true,
  "additional_styles": {
    "display": "flex",
    "gap": "12px",
    "justifyContent": "flex-end"
  },
  "controls": [
    {
      "type": "button",
      "name": "cancel_button",
      "label": "Cancel",
      "hideLabel": true,
      "additional_styles": {"flex": "1"},
      "actions": [...]
    },
    {
      "type": "button",
      "name": "save_button",
      "label": "Save",
      "hideLabel": true,
      "additional_styles": {"flex": "1"},
      "actions": [...]
    }
  ]
}

Highlighted infoscreen with custom background and padding:

{
  "type": "infoscreen",
  "label": "Please review your information before submitting.",
  "additional_styles": {
    "backgroundColor": "#fff8e1",
    "border": "1px solid #f9a825",
    "borderRadius": "6px",
    "padding": "12px 16px",
    "color": "#5d4037"
  }
}

Compact read-only field (reduced width):

{
  "type": "text",
  "id": "policy_number_field",
  "name": "policy_number",
  "label": "Policy #",
  "value": "ANN-20240101-001",
  "readOnly": true,
  "additional_styles": {
    "width": "200px",
    "fontFamily": "monospace"
  }
}

Accepted values follow standard CSS syntax (strings for most properties, numeric strings for unitless values). Property names must be camelCase (marginTop, not margin-top).


on_blur autosave

The following input types support on_blur autosave: text, date, number, phone, textarea, select, multiselect, checkbox. Any of these can fire actions when the user leaves the field:

{
  "type": "text",
  "id": "first_name_field",
  "name": "first_name",
  "label": "First Name",
  "value": "John",
  "on_blur": [
    {
      "type": "invoke",
      "method": "POST",
      "url": "/api/transactions/save_customer_application/",
      "body": {
        "first_name": "{first_name_field}"
      }
    }
  ]
}

Field value references

Inside invoke body and update_value, values are referenced with curly-brace syntax:

Syntax Resolves to
{field_id} Current value of the field with that id
{response.path.to.field} Nested field from the previous invoke response
{row.field_name} Field from the table row that triggered on_row_click

Dashboard Configuration

The dashboard layout is defined in src/fixtures/frontend_config_dashboard.json. This is a separate configuration layer from individual form transactions — it wires together the top-level dashboard UI and is not returned by any transaction function.

Top-level structure

{
  "FrontendConfigDashboard": [
    {
      "title": "PlanEasy Portal",
      "external_id": "default",
      "elements": [...]
    }
  ]
}

Each entry in FrontendConfigDashboard is a named dashboard variant identified by external_id (e.g. "default" for customers, "admin" for admin users).

Dashboard element types

These types appear inside elements arrays and are distinct from the form control types above.

form

Embeds a form loaded dynamically from a transaction's control_source.

{
  "type": "form",
  "title": "My Profile",
  "id": "profile",
  "control_source": {
    "type": "dynamic",
    "dynamic_value": {
      "type": "transaction",
      "entity_name": "get_profile"
    }
  }
}

table

Displays tabular data fetched from a data_source transaction. Supports row-click actions and toolbar buttons.

{
  "id": "applications",
  "type": "table",
  "title": "My Applications",
  "data_source": {
    "type": "transaction",
    "entity_name": "get_customer_applications_table"
  },
  "on_row_click": [
    {
      "type": "update_form",
      "control_source": {
        "type": "dynamic",
        "dynamic_value": {
          "type": "transaction",
          "entity_name": "get_customer_application_form",
          "body": {
            "application_id": "{row.application_id}"
          }
        }
      }
    }
  ],
  "table_actions": [
    {
      "type": "button",
      "name": "create_policy",
      "label": "Create New",
      "hideLabel": true,
      "actions": [
        {
          "type": "update_form",
          "control_source": {
            "type": "dynamic",
            "dynamic_value": {
              "type": "transaction",
              "entity_name": "get_policy_form"
            }
          }
        }
      ]
    }
  ]
}
  • data_source{"type": "transaction", "entity_name": "<function>"} — names a transaction that returns table rows
  • on_row_click — array of actions fired when the user clicks a row; {row.field} references the row's data
  • table_actions — array of button controls rendered in the table toolbar

section (dashboard)

Top-level grouping of dashboard elements (not the same as the form section control). Has title and elements.

{
  "type": "section",
  "title": "Applications",
  "elements": [...]
}

grid

Grid layout container with configurable columns and rows.

{
  "title": "Transaction Overview",
  "type": "grid",
  "columns": 2,
  "rows": 1,
  "elements": [
    {"type": "grid_col", "elements": [...]}
  ]
}

grid_col

A column inside a grid. Contains an elements array.

{
  "type": "grid_col",
  "elements": [...]
}

chart

A chart widget. Supports data_source with aggregation for model-backed data.

{
  "type": "chart",
  "chart_type": "bar",
  "title": "Monthly Transactions",
  "data_source": {
    "type": "class",
    "entity_name": "PaymentTransaction",
    "aggregation": {
      "field": "amount",
      "function": "sum",
      "group_by": "month"
    }
  }
}
  • chart_type — chart variant (e.g. "bar")
  • data_source.type"class" for model aggregation or "transaction" for a named transaction
  • aggregationfield, function (e.g. "sum"), and group_by (e.g. "month")

Form Helpers (form_helpers.py)

src/transactions/form_helpers.py provides factory functions that all form transaction files use to build control dicts. Import from this module instead of building dicts inline.

from transactions.form_helpers import (
    text_field, date_field, number_field, select_field,
    checkbox_field, phone_field, textarea_field, object_field,
    section, submit_button,
)

Functions

Function Signature Returns
text_field (field_id, name, label, value="", *, required=False, placeholder=None) text control dict
date_field (field_id, name, label, value="", *, required=False) date control dict
number_field (field_id, name, label, value="", *, required=False, placeholder=None) number control dict
select_field (field_id, name, label, options, value="", *, required=False) select control dict
checkbox_field (field_id, name, label, value="false", *, required=False) checkbox control dict
phone_field (field_id, name, label, value="", *, required=False) phone control dict
textarea_field (field_id, name, label, value="", *, required=False, placeholder=None) textarea control dict
object_field (field_id, name, label, entity_type, value="", *, required=False) object_latest control dict
section (name, label, controls) section container dict
submit_button (name, label, url, body, success_message, error_message=None) full submit button with POST invoke and success/error popups

Example usage

from transactions.form_helpers import text_field, select_field, section, submit_button

controls = [
    section("personal", "Personal Info", [
        text_field("first_name_field", "first_name", "First Name", required=True),
        select_field("state_field", "state", "State", _STATE_OPTIONS),
    ]),
    submit_button(
        name="save_btn",
        label="Save",
        url="/api/transactions/update_profile/",
        body={"first_name": "{first_name_field}", "state": "{state_field}"},
        success_message="Profile saved successfully.",
    ),
]

submit_button always uses method: POST and automatically wires onSuccess / onError popups when messages are provided.