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
{
"type": "infoscreen",
"label": "The main text that will be displayed"
}
Header¶
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¶
{
"type": "header",
"label": "Header Label",
"value": "Header Text"
}
{
"type": "header",
"label": "",
"value": "Header Text",
"hideLabel": true
}
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 textcontent_type—"text"or"markdown"attachments— array of attachment references ornullcreated_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— theidof another field in the same formcondition— 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,DELETEurl— API endpoint pathbody— key/value map; field values are referenced as{field_id}onSuccess— array of actions run after a successful responseonError— array of actions run on errorasync— boolean; iftrue, 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"
}
navigate_to_section¶
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 rowson_row_click— array of actions fired when the user clicks a row;{row.field}references the row's datatable_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 transactionaggregation—field,function(e.g."sum"), andgroup_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.