Skip to main content

Page Designer Documentation# Page Designer Documentation

  • Renderers Documentation - Component rendering engine- DataView Documentation - Data display configurations- Workflow Designer Documentation - Automation definitions## Related Documentation---9. Monitor → Track usage, collect feedback8. Publish → Save version, make available to users7. Test → Preview on desktop, tablet, mobile6. Style & Theme → Apply CSS variables5. Validate Configuration → Test with sample data4. Bind Data → Connect components to entities/workflows3. Add Components → Drag from palette, configure properties2. Design Layout → Add containers and sections1. Create Page → Select type (Form/Dashboard/Report/Content)## Page Creator Workflow---| Dashboard widgets not refreshing | Cache preventing updates | Invalidate React Query after mutation || Validation not showing errors | Error state not rendered | Check template displays errors object || Nested object fields broken | Incorrect fieldPath syntax | Use dot notation: "address.street" || Form field value not updating | Missing onChange handler | Ensure binding properly wired in renderer || Components overlapping | Layout configuration error | Adjust grid column/row, width/height values || Styling not applying | CSS variable undefined | Check ThemeContext, ensure variable declared || Data not binding to component | Wrong entityId or fieldPath | Verify entity exists, field matches schema || Form not submitting | Validation not passing | Check field validators, console for error details ||-------|-------|----------|| Issue | Cause | Solution |## Common Issues & Solutions---| Configuration editor | components/designer/PropertyPanel.tsx | Inspect properties || Component palette | components/designer/ComponentPalette.tsx | Drag-drop elements || Page service | services/api.ts → pageDesigner.* | API calls || Data binding | hooks/useDataBinding.ts | Links components to data || Report designer | components/viewer/ReportRenderer.tsx | Renders report || Dashboard designer | components/viewer/DashboardRenderer.tsx | Renders dashboard || Form designer | components/viewer/FormRenderer.tsx | Renders form from config ||---------|------|-------|| Purpose | File | Notes |## Key Files & Imports---}; return null; } } return rule.errorMessage || `Validation failed for ${rule.type}`; if (!validationRules[rule.type](value, rule.params)) { for (const rule of fieldConfig.validation.rules || []) { } return 'This field is required'; if (fieldConfig.required && !value) {const validateField = (fieldId, value, fieldConfig) => {// Applied during rendering}; 'maxLength': (value, max) => value.length <= max 'minLength': (value, min) => value.length >= min, 'customPattern': (value, pattern) => new RegExp(pattern).test(value), 'noNumbers': (value) => !/\d/.test(value), 'url': (value) => /^https?:\/\/.+/.test(value), 'phone': (value) => /^[\d\-\(\)\+\s]{10,}$/.test(value), 'email': (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value),const validationRules = {typescriptForm Validation Rules:### Validation Schema</div> ))} </div> <ComponentRenderer config={comp} /> > }} gridRow: `${comp.layout.row} / span ${comp.layout.height}` gridColumn: `${comp.layout.column} / span ${comp.layout.width}`, style={{ key={comp.componentId} <div {components.map(comp => (<div className="page-grid">// CSS Grid approach - 12 column layouttypescriptGrid-based Layout:### Layout Engine} } "value": "Fixed Value" "type": "static", "binding": {{// Static Binding - Hardcoded value} } "expression": "${quantity} * ${unitPrice} * (1 - ${discountPercent}/100)" "type": "computed", "binding": {{// Computed Binding - Formula or expression} } "outputPath": "approvalResult.status" "workflowId": "approve-request", "type": "workflow", "binding": {{// Workflow Binding - Workflow output} } "fieldPath": "email_address" // Can be nested: "address.city" "entityId": "customers", "type": "entity", "binding": {{// Entity Binding - Direct table fieldtypescriptData Binding Types:### Component Binding Pattern} ] } "styling": { /* CSS props */ } "content": "string|object", "type": "paragraph|heading|image|video|section|block-quote", "elementId": "para-1", { "elements": [ }, "enableSearch": false "enableTableOfContents": true, "enableSidebar": false, "layout": "single-column|two-column|three-column", "contentSettings": { "pageType": "content",{jsonContent-Specific Properties:### Content Page Type} ); </div> </div> ))} <ReportSection key={idx} section={section} data={data} /> {pageConfig.sections.map((section, idx) => ( <div ref={rendererRef} className="report-content"> /> onExport={handleExport} formats={pageConfig.reportSettings.exportFormats} <ReportToolbar <div className="report-wrapper"> return ( }; } XLSX.writeFile(wb, 'report.xlsx'); XLSX.utils.book_append_sheet(wb, ws); const wb = XLSX.utils.book_new(); const ws = XLSX.utils.json_to_sheet(data); } else if (exportFormat === 'excel') { html2pdf().set(options).from(rendererRef.current).save(); const doc = new jsPDF(); if (exportFormat === 'pdf') { const handleExport = async (exportFormat) => { const rendererRef = useRef(null);export function ReportRenderer({ pageConfig, data, format = 'html' }) {typescriptReport Renderer Implementation:} ] } ] } "config": { /* type-specific */ } "type": "table|text|image|chart", { "content": [ "type": "header|detail|summary|footer", { "sections": [ }, "exportFormats": ["pdf", "excel", "html"] "pageNumbers": true, "footerTemplate": "html", "headerTemplate": "html", "margins": { "top": 0.5, "right": 0.5, "bottom": 0.5, "left": 0.5 }, "paperSize": "A4|Letter|Legal", "orientation": "portrait|landscape", "reportSettings": { "pageType": "report",{jsonReport-Specific Properties:### Report Page Type} ); </div> </div> ))} /> refreshTime={refreshTime} filters={filters} widget={widget} key={widget.widgetId} <WidgetRenderer {pageConfig.widgets.map(widget => ( <div className="widget-grid"> )} <FilterPanel config={pageConfig} /> {pageConfig.dashboardSettings.enableFilters && ( <div className="dashboard-container"> return ( }, [pageConfig.dashboardSettings.refreshInterval]); return () => clearInterval(interval); ); pageConfig.dashboardSettings.refreshInterval || 30000 () => setRefreshTime(Date.now()), const interval = setInterval( useEffect(() => { // Auto-refresh based on config const [refreshTime, setRefreshTime] = useState(Date.now());export function DashboardRenderer({ pageConfig, filters = {} }) {typescriptDashboard Renderer Implementation:} ] } } "height": 4 "width": 6, "column": 1, "row": 1, "layout": { }, "measure": "amount" "groupBy": "month", "aggregation": "sum", "entityId": "orders", "dataBinding": { "title": "Revenue Trend", "chartType": "line|bar|pie|area|scatter", "widgetType": "chart|metric|table|gauge|map", "widgetId": "widget-1", { "widgets": [ }, "exportFormat": ["pdf", "excel", "json"] "dateRangeFilter": true, "enableSearch": true, "enableFilters": true, "refreshInterval": 30000, "dashboardSettings": { "pageType": "dashboard",{jsonDashboard-Specific Properties:### Dashboard Page Type} ); </form> ))} <FormSection key={section.sectionId} section={section} /> {pageConfig.sections.map(section => ( <form onSubmit={handleSubmit}> return ( }; } setIsSubmitting(false); } finally { await onSubmit(formData); try { setIsSubmitting(true); } return; setErrors(newErrors); if (Object.keys(newErrors).length > 0) { const newErrors = validateForm(formData, pageConfig.sections); e.preventDefault(); const handleSubmit = async (e) => { }; setErrors(prev => ({ ...prev, [fieldId]: null })); // Clear error for this field as user corrects setFormData(prev => ({ ...prev, [fieldId]: value })); const handleFieldChange = (fieldId, value) => { const [isSubmitting, setIsSubmitting] = useState(false); const [errors, setErrors] = useState({}); const [formData, setFormData] = useState(data);export function FormRenderer({ pageConfig, onSubmit, data = {} }) {typescriptForm Renderer Implementation:- Display: label, divider, alert, instructions- Advanced: file-upload, rich-text, nested-object, array- Select: dropdown, multi-select, radio, checkbox- Input: text, email, password, number, date, time, urlForm Field Types:} ] } ] } } "customValidators": ["noNumbers"] "maxLength": 50, "minLength": 2, "pattern": "string", "validation": { "required": true, "fieldType": "text", "fieldName": "First Name", "fieldId": "firstName", { "fields": [ "sectionTitle": "Personal Information", "sectionId": "section-1", { "sections": [ }, "progressType": "steps|linear|percentage" "showProgressIndicator": true, "successMessage": "Form submitted successfully", "submitTarget": "workflow|entity|api", "submitAction": "save|submit|next", "submitButtonText": "Submit", "formSettings": { "pageType": "form",{jsonForm-Specific Properties:### Form Page Type## Technical Details---- Implement custom styling outside CSS variables: Breaks theming- Use ambiguous field names: e.g., avoid "field1", "data2"- Create unversioned page changes: Always track modifications- Skip testing with actual data: Test with production-like datasets- Use components without data source: Every data display needs binding- Create circular data dependencies: Component A binds to B, B to A- Ignore accessibility requirements: Use semantic HTML, ARIA labels- Forget error message specifications: Define what shows on validation fail- Mix multiple layout types in same page: Use consistent grid or flex- Use inline styles for brand colors: Use CSS variable theme system- Create deeply nested component trees: Keep structure flat (max 3 levels)- Hardcode data values in components: Always use data bindings### ❌ DON'T- Validate form before submission: Client-side checks reduce errors- Use CSS variables for styling: Ensures theme consistency- Document complex field mappings: Especially for nested objects- Version your page configurations: Track changes and enable rollback- Use standardized container types: card, panel, section for visual hierarchy- Implement default values where applicable: Pre-fill common data- Provide helpful labels and placeholders: Guide user input- Use conditional visibility: Show/hide components based on values- Group related fields in sections: Improves UX and layout clarity- Test forms on multiple screen sizes: Ensure responsive design- Specify validation rules per field: Use built-in validators- Use data binding to external entities: Connect components to database fields### ✅ DO## Do's and Don'ts---- Variable Names: camelCase in data bindings (e.g., customerName, orderTotal)- Containers: Numbered sections (e.g., "Section 1", "Section 2") or semantic names- Fields: lowercase_with_underscores (e.g., email_address, total_revenue)- Components: Functional names (e.g., "Email Input", "Revenue Chart", "User Table")- Pages: Descriptive names (e.g., "User Registration Form", "Sales Dashboard")### Naming Conventions └── usePageConfig.ts # CRUD page configurations ├── useDataBinding.ts # Bind components to data ├── useDesignerState.ts # Designer state management└── hooks/│ └── layoutEngine.ts # Render logic│ ├── componentValidator.ts # Config validation│ ├── pageDesigner.service.ts # API calls├── services/│ └── Toolbar.tsx # Actions│ ├── Canvas.tsx # Drop zone│ ├── PropertyPanel.tsx # Inspect/edit│ ├── ComponentPalette.tsx # Available components├── components/│ └── ElementLibrary.tsx│ ├── ContentRenderer.tsx│ ├── ContentDesigner.tsx│ └── content/ # Content page builder│ │ └── ReportTemplates.tsx│ │ ├── ReportRenderer.tsx│ │ ├── ReportDesigner.tsx│ ├── report/ # Report components│ │ └── WidgetLibrary.tsx│ │ ├── DashboardRenderer.tsx│ │ ├── DashboardDesigner.tsx│ ├── dashboard/ # Dashboard components│ │ └── PropertyPanel.tsx│ │ ├── FieldPalette.tsx│ │ ├── FormRenderer.tsx│ │ ├── FormDesigner.tsx│ ├── form/ # Form designer├── pages/designer/### File Organization} } "version": "1.0" "modifiedDate": "ISO8601", "modifiedBy": "string", "createdDate": "ISO8601", "createdBy": "string", "metadata": { }, "filteredFields": ["field1", "field2"] "entityId": "string", "dataBindings": { ], } "children": [ /* nested components */ ] }, "visibility": "condition|always|never" "inlineStyles": { /* CSS variables */ }, "cssClass": "string", "styling": { }, "fieldName": "string" "entityId": "string", "dataSource": "entity|workflow|static", "binding": { "properties": { /* component-specific */ }, "componentName": "Display Name", "componentType": "FormField|Chart|Table|Button|Section|...", "componentId": "string", { "components": [ }, "containerType": "default|card|section" "spacing": "md", "columns": 12, "type": "grid|flex|absolute", "layout": { "pageType": "form|dashboard|report|content", "pageName": "string", "pageId": "string",{jsonPage Configuration Schema:### Configuration Structure└─────────────────────────────────────────────────────────┘│ └─ Export support └─ Branding ││ ├─ Sorting ├─ Sections ││ ├─ Grouping ├─ Images ││ ├─ Formatted data ├─ Rich text ││ 3. REPORT Pages 4. CONTENT ││ └─ Multi-step ││ ├─ Submission └─ Key metrics ││ ├─ Validation ├─ Multiple charts ││ ├─ User input ├─ Data visualization ││ 1. FORM Pages 2. DASHBOARD Pages ││ │├─────────────────────────────────────────────────────────┤│ PAGE DESIGNER │┌─────────────────────────────────────────────────────────┐Four Main Page Types:### Page Designer Architecture## Standards---Key Purpose: Enable users to visually design forms, dashboards, reports, and content layouts that automatically render with proper data binding, validation, and interactivity.The Page Designer system is the core visual builder that empowers non-technical users to create sophisticated forms, dashboards, reports, and content pages without writing code. Users build layouts by dragging components, configuring properties, and connecting to data sources. The system generates metadata that the app interprets to render fully-functional pages.## Overview (Non-Technical)

Overview (Non-Technical)

The Page Designer is a visual configuration tool that allows non-technical users to create and manage different types of pages in the Axiom Genesis platform. Whether you're building data entry forms, displaying reports, creating dashboards, or presenting content—the Page Designer handles it all through an intuitive drag-and-drop interface backed by JSON configuration.

Key Purpose: Enable users to design and configure pages (Forms, Dashboards, Reports, Content) visually without writing code, storing configurations that are rendered at runtime.


Standards

Page Types & Architecture

Form Pages:

{
"pageId": "frm_user_profile_v1",
"pageType": "form",
"pageName": "User Profile",
"description": "Manage user account information",
"configuration": {
"layout": "vertical|grid|tabs",
"sections": [
{
"sectionId": "sec_personal",
"title": "Personal Information",
"fields": [
{
"fieldId": "field_firstname",
"fieldName": "firstName",
"fieldLabel": "First Name",
"fieldType": "text",
"required": true,
"validation": {
"minLength": 1,
"maxLength": 100,
"pattern": "^[a-zA-Z\\s]+$"
},
"placeholder": "Enter first name"
}
]
}
],
"actions": [
{ "actionId": "save", "label": "Save", "type": "save" },
{ "actionId": "cancel", "label": "Cancel", "type": "cancel" }
]
},
"createdDate": "2026-03-12T10:00:00Z",
"modifiedDate": "2026-03-12T10:00:00Z"
}

Dashboard Pages:

{
"pageId": "dsh_sales_overview",
"pageType": "dashboard",
"pageName": "Sales Dashboard",
"configuration": {
"layout": "grid",
"gridSize": "2x2",
"widgets": [
{
"widgetId": "widget_revenue",
"widgetType": "card|chart|table|metric",
"title": "Total Revenue",
"dataSource": "dataviewId",
"visualization": "line|bar|pie|table",
"position": { "row": 0, "col": 0, "width": 1, "height": 1 }
}
]
}
}

Report Pages:

{
"pageId": "rpt_monthly_sales",
"pageType": "report",
"pageName": "Monthly Sales Report",
"configuration": {
"sections": [
{
"type": "header",
"content": "Sales Report - March 2026"
},
{
"type": "table",
"dataSource": "dataviewId",
"columns": [
{ "fieldName": "date", "label": "Date" },
{ "fieldName": "revenue", "label": "Revenue", "format": "currency" }
]
},
{
"type": "summary",
"metrics": ["total", "average", "count"]
}
],
"styling": {
"theme": "professional|minimal|colorful",
"pageSize": "A4|Letter",
"orientation": "portrait|landscape"
}
}
}

Content Pages:

{
"pageId": "cnt_about_us",
"pageType": "content",
"pageName": "About Us",
"configuration": {
"sections": [
{
"type": "heading",
"level": 1,
"content": "About Axiom Genesis"
},
{
"type": "richtext",
"content": "HTML or Markdown content here"
},
{
"type": "image",
"src": "/images/team.jpg",
"alt": "Team photo"
}
]
}
}

Configuration Structure Standards

Field Type Support:

{
"text": { "baseType": "string", "inputType": "text", "examples": ["text", "email", "password", "url"] },
"number": { "baseType": "number", "inputType": "number", "examples": ["integer", "decimal", "currency", "percentage"] },
"date": { "baseType": "date", "inputType": "date", "format": "YYYY-MM-DD" },
"time": { "baseType": "time", "inputType": "time", "format": "HH:mm" },
"datetime": { "baseType": "datetime", "inputType": "datetime-local", "format": "YYYY-MM-DD HH:mm" },
"select": { "baseType": "string", "inputType": "select", "options": [{"label": "", "value": ""}] },
"multiselect": { "baseType": "array", "inputType": "select", "multiple": true },
"checkbox": { "baseType": "boolean", "inputType": "checkbox" },
"radio": { "baseType": "string", "inputType": "radio", "options": [{"label": "", "value": ""}] },
"textarea": { "baseType": "string", "inputType": "textarea", "rows": 4 },
"file": { "baseType": "file", "inputType": "file", "accept": ".pdf,.jpg,.png" },
"repeater": { "baseType": "array", "inputType": "repeater", "itemTemplate": {} },
"lookup": { "baseType": "string|array", "inputType": "lookup", "dataSource": "entityId" },
"nested": { "baseType": "object", "inputType": "group", "fields": [] }
}

Validation Standards:

{
"required": true,
"minLength": 1,
"maxLength": 256,
"min": 0,
"max": 1000,
"pattern": "^[a-zA-Z0-9]+$",
"email": true,
"url": true,
"custom": {
"rule": "function_name_in_validators",
"message": "Custom error message"
},
"conditional": {
"condition": "fieldName === 'value'",
"rules": { "required": true }
}
}

Naming Conventions

  • Page IDs: [pageType]_[description]_v[version] (e.g., frm_user_profile_v1, dsh_sales_v2)
  • Section IDs: sec_[description] (e.g., sec_personal, sec_address)
  • Field IDs: field_[fieldName] (e.g., field_firstName, field_email)
  • Widget IDs: widget_[widgetType]_[description] (e.g., widget_chart_revenue)
  • JSON Keys: camelCase (e.g., fieldName, fieldLabel, fieldType)

Do's and Don'ts

✅ DO

  • Use semantic field types: Choose the most specific type (email instead of text for emails)
  • Validate required fields explicitly: Set required: true for mandatory fields
  • Group related fields in logical sections: Organize by entity or workflow step
  • Provide clear field labels: User-friendly labels, not database column names
  • Set reasonable default values: Pre-populate common selections
  • Add helper text for complex fields: Use help property to guide users
  • Enforce data type validation: Configure min/max length, patterns, formats
  • Use lookup fields for relationships: Link to other entities instead of free text
  • Create responsive layouts: Support mobile, tablet, and desktop views
  • Version your page configurations: Increment version when making breaking changes
  • Use conditional visibility: Show/hide fields based on other field values
  • Document complex validation rules: Add comments to custom validators

❌ DON'T

  • Use generic text fields for everything: Reduces data quality and UX
  • Create overly complex nested structures: Keep nesting 2-3 levels max
  • Forget to set field placeholders: Helps users understand expected format
  • Allow free-text where dropdowns work: Use select fields for predefined options
  • Ignore mobile responsiveness: Test layouts on all screen sizes
  • Create too many fields per page: Consider pagination or tabs
  • Change field types in existing configurations: Breaks existing data
  • Store sensitive data (passwords) in configuration: Handle securely in middleware
  • Hardcode database column names as labels: Use user-friendly labels
  • Skip validation formatting: Invalid data wastes time in workflows
  • Create interdependent fields without documenting: Confusion for users
  • Ignore accessibility standards: Ensure keyboard navigation, ARIA labels

Technical Details

Form Rendering Pipeline

1. Page Config Loaded
└─ Fetch from FRM_FORMS table in config DB

2. Field Initialization
├─ Parse field definitions
├─ Load lookup options (from dataviews or entity data)
├─ Apply conditional visibility rules
└─ Initialize validation rules

3. Component Rendering
├─ Map field types to React components
├─ Apply styling (CSS variables from ThemeContext)
├─ Bind event handlers (onChange, onBlur, onFocus)
└─ Attach validation feedback displays

4. User Interaction
├─ User enters data
├─ onChange handler updates form state
├─ Real-time validation executes
├─ Conditional fields update visibility
└─ Dependent fields update options

5. Form Submission
├─ Validate all fields (including custom rules)
├─ Transform data (field name mapping, nested object construction)
├─ POST to API with tenant context
└─ Handle response (success/error feedback)

Form Component Structure

FormRenderer (High-Level):

interface FormRendererProps {
pageId: string; // e.g., 'frm_user_profile_v1'
initialData?: object; // Pre-populate form
readonly?: boolean; // Disable editing
onSubmit: (data: object) => Promise<void>;
onCancel?: () => void;
}

// Returns: Fully rendered, interactive form with validation
<FormRenderer pageId={pageId} onSubmit={handleSave} />

Field Component Mapping:

// Based on fieldType, render appropriate component
fieldType → Component
'text'<TextInput />
'email'<EmailInput /> with email validation
'date'<DatePicker /> with format YYYY-MM-DD
'select'<SelectDropdown /> with options fetched
'checkbox'<CheckboxGroup /> with multiple selection
'repeater'<RepeaterGroup /> with add/remove row functionality
'lookup'<LookupDropdown /> with data from entity
'nested'<NestedGroup /> with collapsible sections

Validation Execution Model

Real-Time Validation (on blur):

function validateField(fieldId, value, validationRules) {
const errors = [];

// Run each validation rule
if (validationRules.required && !value) {
errors.push('This field is required');
}

if (validationRules.minLength && value.length < validationRules.minLength) {
errors.push(`Minimum length: ${validationRules.minLength}`);
}

if (validationRules.pattern && !new RegExp(validationRules.pattern).test(value)) {
errors.push('Invalid format');
}

if (validationRules.custom) {
const customError = runCustomValidator(validationRules.custom, value);
if (customError) errors.push(customError);
}

if (validationRules.conditional) {
// Check condition against other field values
if (evaluateCondition(validationRules.conditional.condition)) {
// Apply conditional rules
}
}

return errors;
}

Form-Level Validation (on submit):

async function validateForm(formData, configuration) {
const errors = {};

for (const section of configuration.sections) {
for (const field of section.fields) {
const fieldErrors = validateField(
field.fieldId,
formData[field.fieldName],
field.validation
);

if (fieldErrors.length > 0) {
errors[field.fieldId] = fieldErrors;
}
}
}

// Custom cross-field validation
const crossFieldErrors = validateCrossFields(formData, configuration);
Object.assign(errors, crossFieldErrors);

// If any errors, prevent submission
if (Object.keys(errors).length > 0) {
throw new ValidationError('Form has errors', errors);
}

return true;
}

Conditional Visibility & Dependencies

Configuration Example:

{
"fieldId": "field_department",
"fieldName": "department",
"fieldLabel": "Department",
"fieldType": "select",
"visibility": {
"condition": "employmentStatus === 'active'",
"dependsOn": ["employmentStatus"]
}
}

Runtime Evaluation:

function evaluateVisibility(field, formData) {
if (!field.visibility) return true;

// Replace field references with actual values
let condition = field.visibility.condition;
field.visibility.dependsOn.forEach(depField => {
condition = condition.replace(
depField,
`'${formData[depField]}'`
);
});

// Safely evaluate condition (use VM2 for security)
return eval(condition);
}

Data Transformation (Field Name Mapping)

Configuration Mapping:

{
"fieldId": "field_firstname",
"fieldName": "firstName", // Database column
"displayName": "First Name", // UI label
"dataMapping": {
"source": "user.profile.firstName", // If nested in payload
"transform": "trim|uppercase|parseFloat" // Data transformation
}
}

Transformation on Submit:

function transformFormData(rawData, configuration) {
const result = {};

for (const section of configuration.sections) {
for (const field of section.fields) {
let value = rawData[field.fieldId];

// Apply transformations
if (field.dataMapping?.transform) {
value = applyTransformations(value, field.dataMapping.transform);
}

// Map to database field name
const dbFieldName = field.fieldName;
result[dbFieldName] = value;
}
}

return result;
}

Repeater Fields (Dynamic Rows)

Configuration Structure:

{
"fieldId": "field_addresses",
"fieldName": "addresses",
"fieldType": "repeater",
"label": "Addresses",
"minRows": 1,
"maxRows": 5,
"itemTemplate": {
"fields": [
{
"fieldId": "field_address_street",
"fieldName": "street",
"fieldType": "text",
"required": true
},
{
"fieldId": "field_address_city",
"fieldName": "city",
"fieldType": "text",
"required": true
}
]
}
}

Repeater Component Behavior:

// Data structure in form
{
addresses: [
{ street: "123 Main St", city: "New York" },
{ street: "456 Oak Ave", city: "Boston" }
]
}

// Component allows:
✓ Add new row (up to maxRows)
✓ Remove row (minimum minRows)
✓ Reorder rows (drag-drop)
✓ Edit fields within row
✓ Validate each row independently

Form, Dashboard, Report, Content Details

Form Configuration

  • Best For: Data entry, user profiles, settings, creation workflows
  • Key Components: Input fields, validation, submission actions
  • Data Binding: Two-way binding to form state
  • Submission: POST to /api/v1/records with entity context
  • Responses: Success message + navigation or error display

Dashboard Configuration

  • Best For: KPI visualization, status monitoring, analytics overview
  • Key Components: Cards, charts, tables, metrics widgets
  • Data Refresh: Configurable interval or manual refresh
  • Interactivity: Drill-down, filtering, date range selection
  • Export: CSV/PDF export capability for charts

Report Configuration

  • Best For: Formal documentation, compliance reporting, data export
  • Key Components: Tables, summaries, headers, footers
  • Sorting & Grouping: Configure grouping levels and sort order
  • Pagination: Page breaks, page numbers, header/footer on each page
  • Export: PDF/Excel export with styling preserved

Content Configuration

  • Best For: Help pages, announcements, static information, documentation
  • Key Components: Rich text, images, embedded media
  • Markdown Support: Full Markdown support with sanitization
  • Version Control: Track content changes, rollback capability
  • Multi-Language: Content localization keys support

Common Patterns

Login Form Pattern

{
"pageId": "frm_login",
"pageType": "form",
"sections": [{
"fields": [
{ "fieldName": "email", "fieldType": "email", "required": true },
{ "fieldName": "password", "fieldType": "password", "required": true }
]
}],
"actions": [
{ "label": "Login", "type": "submit", "api": "/api/v1/auth/login" },
{ "label": "Forgot Password?", "type": "link", "href": "/forgot-password" }
]
}

Multi-Step Form Pattern

{
"pageId": "frm_registration",
"pageType": "form",
"layout": "tabs",
"sections": [
{ "title": "Step 1: Personal Info", "fields": [...] },
{ "title": "Step 2: Address", "fields": [...] },
{ "title": "Step 3: Preferences", "fields": [...] }
]
}

Conditional Fields Pattern

{
"fieldId": "field_employmentType",
"fieldType": "select",
"options": [
{ "label": "Full-Time", "value": "fulltime" },
{ "label": "Contract", "value": "contract" }
]
},
{
"fieldId": "field_contractEndDate",
"fieldType": "date",
"visibility": { "condition": "employmentType === 'contract'" }
}

Key Files & References

PurposeFileNotes
Form schemasconfig DB: FRM_FORMSPage configurations stored as JSON
Field validatorsmiddleware: utils/validators.util.jsValidation rule library
Form rendererfrontend: components/form/FormRenderer.tsxMain rendering component
Type definitionsfrontend: types/form.types.tsTypeScript interfaces
API clientfrontend: services/api.tsForm submission API calls