Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.encoreos.io/llms.txt

Use this file to discover all available pages before exploring further.

Overview

The ConfigurableForm component (PF-17) provides a dynamic, configuration-driven form renderer that eliminates the need for hardcoded forms. It supports:
  • Standard fields from entity table columns
  • Custom fields from pf_custom_field_definitions
  • Picklist-based selects from pf_picklists
  • Lookup fields for entity references (accounts, customers, etc.)
  • Field visibility based on roles and conditional rules
  • Configurable layouts with sections and column spans

Quick Start

Basic Usage

import { ConfigurableForm } from '@/platform/field-config';

function MyEntityForm({ entity, onSubmit, onCancel }) {
  return (
    <ConfigurableForm
      entityType="fa_customers"
      viewContext={entity ? 'edit_form' : 'create_form'}
      initialData={entity || {}}
      onSubmit={onSubmit}
      onCancel={onCancel}
      isSubmitting={false}
      submitLabel="Save Customer"
    />
  );
}

Props

PropTypeRequiredDescription
entityTypestringYesEntity type key (e.g., fa_customers, hr_employees)
viewContextViewContextNoOne of: create_form, edit_form, detail_view, list_view. Defaults to edit_form
initialDataRecord<string, any>NoInitial values for form fields
onSubmit(data) => voidYesCallback when form is submitted
onCancel() => voidNoCallback when Cancel button is clicked
userRolestringNoCurrent user’s role for field visibility checks
isSubmittingbooleanNoShows loading state on submit button
submitLabelstringNoCustom text for submit button

Field Types

Standard Field Types

The following types are supported in STANDARD_FIELDS:
TypeRendererDescription
text<Input>Basic text input
email<Input type="email">Email input with validation
number<Input type="number">Numeric input
date<Input type="date">Date picker
datetime<Input type="datetime-local">Date and time picker
boolean<Switch>Toggle switch
textarea<Textarea>Multi-line text
select<PicklistSelectRenderer>Dropdown using picklist data
lookup<LookupFieldRenderer>Entity reference selector

Adding Select Fields with Picklists

To use a picklist for a select field, add picklistName to the field definition:
// In types.ts STANDARD_FIELDS
{
  field_key: 'customer_type',
  label: 'Customer Type',
  type: 'select',
  picklistName: 'fa_customer_type' // Links to pf_picklists.name
}

Adding Lookup Fields

Lookup fields reference other entities. Configure them in LookupFieldRenderer.tsx:
const LOOKUP_CONFIGS = {
  'default_revenue_account_id': {
    table: 'fa_accounts',
    valueField: 'id',
    labelField: 'name',
    displayFormat: (item) => `${item.account_number} - ${item.name}`,
    filter: { account_type: 'revenue' },
  },
};

Database Configuration

Field Configs Table (pf_entity_field_configs)

Each visible field needs a config entry:
INSERT INTO pf_entity_field_configs (
  organization_id,
  entity_type,
  view_context,
  field_source,
  field_key,
  is_visible,
  is_editable,
  display_order,
  column_span,
  field_section
) VALUES (
  'your-org-id',
  'fa_customers',
  'edit_form',
  'standard',
  'customer_name',
  true,
  true,
  1,
  1,
  'Basic Information'
);

Custom Fields

Custom fields are automatically included when:
  1. They exist in pf_custom_field_definitions for the entity type
  2. A config entry exists with field_source = 'custom'

Migration Example

Converting an existing form to ConfigurableForm:

Before (Hardcoded)

export function CustomerForm({ customer, onSubmit, onCancel }) {
  const form = useForm({...});
  
  return (
    <Form {...form}>
      <FormField name="customer_name" ... />
      <FormField name="customer_type" ... />
      {/* 50+ lines of form fields */}
    </Form>
  );
}

After (ConfigurableForm)

export function CustomerForm({ customer, onSubmit, onCancel, isSubmitting }) {
  return (
    <ConfigurableForm
      entityType="fa_customers"
      viewContext={customer ? 'edit_form' : 'create_form'}
      initialData={customer}
      onSubmit={onSubmit}
      onCancel={onCancel}
      isSubmitting={isSubmitting}
    />
  );
}

Fallback Behavior

If no field configs exist for an entity/context, ConfigurableForm falls back to rendering all fields from STANDARD_FIELDS in a default layout.

Best Practices

  1. Seed field configs for all view contexts - create_form, edit_form, detail_view, list_view
  2. Use meaningful section names - Groups fields visually in the UI
  3. Set column_span = 2 for full-width fields like textarea
  4. Configure conditional_rules for dependent field visibility
  5. Use picklists instead of hardcoded select options for flexibility

Architecture

ConfigurableForm
    └── DynamicFormRenderer
            ├── useEntityFieldConfigs (fetch configs)
            ├── useConditionalVisibility (show/hide logic)
            └── For each field:
                    ├── StandardFieldRenderer (text, email, number, etc.)
                    │       ├── PicklistSelectRenderer (select with picklist)
                    │       └── LookupFieldRenderer (entity references)
                    └── CustomFieldInput (custom fields)