Skip to main content

Page Configuration Documentation

Module: Form & Page Design Configuration
Version: 1.0
Last Updated: March 12, 2026


📌 Overview

Page Configuration manages form and page layouts in Axiom Genesis. It defines:

  • Page structure: sections, containers, grid layouts
  • Form fields: input types, validation rules, dependencies
  • Field rendering: labels, placeholders, styling
  • Form actions: submit buttons, workflows triggered
  • Data binding: linking forms to entities via object profiles

Core Table: app_page_configs


app_page_configs - Page Formation Configuration

Purpose

Stores all form and page configurations including schema, layout, fields, and validation rules. Both forms (FormRenderer) and pages (PageViewer) use this table.

Schema

SELECT
pgc_id (INT), -- Primary Key
pgc_tenant_id (INT), -- Tenant isolation
pgc_code (VARCHAR(256)), -- Page identifier (e.g., 'FRM_CUSTOMERS')
pgc_description (VARCHAR), -- Display name
pgc_app_code (VARCHAR), -- Application context
pgc_config (JSON), -- Page metadata & layout
pgc_page_schema (JSON), -- Form schema with fields
pgc_status (INT), -- 1=Active, 0=Inactive
pgc_is_deleted (BIT), -- Soft delete flag
pgc_audit_data (JSON), -- Audit trail
pgc_add_dtls (JSON) -- Additional metadata
FROM app_page_configs
WHERE pgc_status = 1 AND pgc_is_deleted = 0

pgc_config Structure (JSON) - Page Metadata

Defines overall page structure, layout, and rendering options:

{
"pageCode": "FRM_CUSTOMERS",
"pageName": "Customer Registration Form",
"pageType": "form", // 'form', 'detail', 'view', 'dashboard'
"entityCode": "CUSTOMERS", // Links to app_object_registries
"profileCode": "CUSTOMERS_EDIT", // Links to app_object_profiles
"layout": {
"type": "grid", // 'grid', 'flex', 'tab'
"columns": 2, // 2-column layout
"spacing": "medium" // small, medium, large
},
"rendering": {
"theme": "light",
"orientation": "vertical"
},
"actions": {
"submit": {
"label": "Save Customer",
"style": "primary",
"workflowCode": null // Optional: trigger workflow on submit
},
"cancel": {
"label": "Cancel",
"style": "secondary"
},
"reset": {
"label": "Clear Form",
"style": "tertiary"
}
},
"validation": {
"validateOnBlur": true,
"validateOnChange": false,
"validateOnSubmit": true,
"showValidationSummary": true
},
"styling": {
"fieldLabelWidth": "120px",
"containerClass": "form-container",
"fieldClass": "form-field"
}
}

pgc_page_schema Structure (JSON) - Form Schema + Fields

The complete form definition with all fields, sections, validation, and relationships:

{
"schemaVersion": "1.0",
"formId": "FRM_CUSTOMERS",
"formName": "Customer Registration",
"description": "Complete customer profile form",

"sections": [
{
"sectionId": "sec_personal",
"sectionName": "Personal Information",
"sectionOrder": 1,
"sectionType": "default",
"fields": [
{
"fieldId": "cus_name",
"fieldName": "Customer Name",
"fieldLabel": "Full Name",
"fieldType": "text", // 'text', 'email', 'number', 'select', 'checkbox', 'date', 'textarea', 'file'
"dataType": "VARCHAR(256)",
"isRequired": true,
"isReadOnly": false,
"placeholder": "Enter customer name",
"helpText": "Customer's legal full name",
"validation": {
"minLength": 1,
"maxLength": 256,
"pattern": null,
"customRule": null,
"errorMessage": "Name is required and must be 1-256 chars"
},
"defaultValue": null,
"fieldOrder": 1,
"fieldWidth": "50%" // CSS width
},
{
"fieldId": "cus_email",
"fieldName": "Email Address",
"fieldLabel": "Email",
"fieldType": "email",
"dataType": "VARCHAR(256)",
"isRequired": true,
"isReadOnly": false,
"validation": {
"pattern": "^[^@]+@[^@]+\\.[^@]+$",
"customRule": "email_unique", // Custom validation function
"errorMessage": "Valid email required and must be unique"
},
"fieldOrder": 2,
"fieldWidth": "50%"
},
{
"fieldId": "cus_phone",
"fieldName": "Phone Number",
"fieldLabel": "Phone",
"fieldType": "text",
"dataType": "VARCHAR(20)",
"isRequired": false,
"validation": {
"pattern": "^\\+?[0-9\\-\\(\\)\\s]+$",
"errorMessage": "Invalid phone format"
},
"fieldOrder": 3,
"fieldWidth": "50%"
}
]
},
{
"sectionId": "sec_details",
"sectionName": "Account Details",
"sectionOrder": 2,
"fields": [
{
"fieldId": "cus_status",
"fieldName": "Account Status",
"fieldLabel": "Status",
"fieldType": "select",
"dataType": "VARCHAR(50)",
"isRequired": true,
"options": [
{ "label": "Active", "value": "ACTIVE" },
{ "label": "Inactive", "value": "INACTIVE" },
{ "label": "Suspended", "value": "SUSPENDED" }
],
"defaultValue": "ACTIVE",
"fieldOrder": 1,
"fieldWidth": "50%"
},
{
"fieldId": "cus_credit_limit",
"fieldName": "Credit Limit",
"fieldLabel": "Credit Limit",
"fieldType": "number",
"dataType": "DECIMAL(10,2)",
"isRequired": false,
"validation": {
"minValue": 0,
"maxValue": 1000000,
"errorMessage": "Credit limit must be between 0 and 1,000,000"
},
"fieldOrder": 2,
"fieldWidth": "50%",
"visibility": {
"dependsOn": "cus_status",
"showWhen": "ACTIVE" // Only show if status = ACTIVE
}
},
{
"fieldId": "cus_notes",
"fieldName": "Internal Notes",
"fieldLabel": "Notes",
"fieldType": "textarea",
"dataType": "VARCHAR(MAX)",
"isRequired": false,
"validation": {
"maxLength": 5000
},
"fieldOrder": 3,
"fieldWidth": "100%",
"visibility": {
"roleRequired": "ADMIN" // Only admins see this
}
}
]
}
],

"containers": [
{
"containerId": "cont_actions",
"containerType": "button-group",
"location": "footer",
"buttons": [
{
"buttonId": "btn_submit",
"buttonLabel": "Save Customer",
"buttonType": "submit",
"style": "primary",
"action": "submit"
},
{
"buttonId": "btn_cancel",
"buttonLabel": "Cancel",
"buttonType": "button",
"style": "secondary",
"action": "goBack"
}
]
}
],

"fieldGroups": [
// Grouped field definitions
{
"groupId": "grp_contact",
"groupName": "Contact Information",
"fields": ["cus_email", "cus_phone"]
}
],

"conditionalVisibility": [
// Dynamic field visibility rules
{
"rule": "If cus_status = 'ACTIVE' show cus_credit_limit",
"field": "cus_credit_limit",
"condition": {
"field": "cus_status",
"operator": "equals",
"value": "ACTIVE"
},
"action": "show"
},
{
"rule": "If user role = 'ADMIN' show cus_notes",
"field": "cus_notes",
"condition": {
"field": "@user.role",
"operator": "equals",
"value": "ADMIN"
},
"action": "show"
}
],

"fieldDependencies": [
{
"dependentField": "cus_credit_limit",
"dependsOnField": "cus_status",
"action": "enable_if_equals",
"value": "ACTIVE"
}
]
}

Field Type Reference

TypeData typesValidationComponent
textVARCHAR, CHARmin/max length, patternText input
emailVARCHARemail patternEmail input
numberINT, DECIMAL, FLOATmin/max valueNumber input
dateDATE, DATETIMEdate rangeDate picker
selectVARCHAR, INToptions, requiredDropdown/select
checkboxBITtrue/falseCheckbox
radioVARCHARoptionsRadio buttons
textareaVARCHAR(MAX), TEXTmax lengthText area
fileVARBINARY, FILE_IDfile type, max sizeFile upload
passwordVARCHARmin length, strengthPassword input
jsonJSONJSON structureJSON editor

Real Example: Customer Form (Complete)

{
"pgc_id": 301,
"pgc_tenant_id": 1,
"pgc_code": "FRM_CUSTOMERS",
"pgc_description": "Customer Registration Form",
"pgc_app_code": "app_main",
"pgc_status": 1,
"pgc_config": {
"pageCode": "FRM_CUSTOMERS",
"pageName": "Customer Registration Form",
"pageType": "form",
"entityCode": "CUSTOMERS",
"profileCode": "CUSTOMERS_EDIT",
"layout": {
"type": "grid",
"columns": 2,
"spacing": "medium"
},
"actions": {
"submit": {
"label": "Save Customer",
"style": "primary"
},
"cancel": {
"label": "Cancel",
"style": "secondary"
}
},
"validation": {
"validateOnSubmit": true,
"showValidationSummary": true
}
},
"pgc_page_schema": {
"schemaVersion": "1.0",
"formId": "FRM_CUSTOMERS",
"formName": "Customer Registration",
"sections": [
{
"sectionId": "sec_personal",
"sectionName": "Personal Information",
"sectionOrder": 1,
"fields": [
{
"fieldId": "cus_name",
"fieldName": "Customer Name",
"fieldLabel": "Full Name",
"fieldType": "text",
"dataType": "VARCHAR(256)",
"isRequired": true,
"validation": {
"minLength": 1,
"maxLength": 256,
"errorMessage": "Name is required"
},
"fieldOrder": 1,
"fieldWidth": "50%"
},
{
"fieldId": "cus_email",
"fieldName": "Email Address",
"fieldLabel": "Email",
"fieldType": "email",
"dataType": "VARCHAR(256)",
"isRequired": true,
"validation": {
"pattern": "^[^@]+@[^@]+\\.[^@]+$",
"errorMessage": "Valid email required"
},
"fieldOrder": 2,
"fieldWidth": "50%"
}
]
},
{
"sectionId": "sec_details",
"sectionName": "Account Details",
"sectionOrder": 2,
"fields": [
{
"fieldId": "cus_status",
"fieldName": "Account Status",
"fieldLabel": "Status",
"fieldType": "select",
"dataType": "VARCHAR(50)",
"isRequired": true,
"options": [
{ "label": "Active", "value": "ACTIVE" },
{ "label": "Inactive", "value": "INACTIVE" }
],
"defaultValue": "ACTIVE",
"fieldOrder": 1,
"fieldWidth": "50%"
}
]
}
]
},
"pgc_audit_data": {
"created_by": "admin",
"created_at": "2026-01-20T09:00:00Z",
"updated_by": "admin",
"updated_at": "2026-03-10T14:30:00Z"
}
}

Form Rendering in Frontend (FormRenderer)

How FormRenderer Uses pgc_page_schema

// From: axiom-genesis-frontend/src/components/FormRenderer.tsx

const FormRenderer = ({ pageCode, entityCode, recordId, user }) => {
const [pageConfig, setPageConfig] = useState(null);

// 1. Load page configuration
useEffect(() => {
const loadPageConfig = async () => {
// Fetch from API
const response = await api.get(`/api/v1/page-configs/${pageCode}`);
const { pgc_page_schema, pgc_config } = response.data;

// pgc_page_schema contains complete form definition
setPageConfig({
sections: pgc_page_schema.sections,
fields: pgc_page_schema.sections.flatMap((s) => s.fields),
conditionalVisibility: pgc_page_schema.conditionalVisibility,
});
};

loadPageConfig();
}, [pageCode]);

// 2. Render form sections
return (
<form onSubmit={handleSubmit}>
{pageConfig.sections.map((section) => (
<div key={section.sectionId} className="form-section">
<h3>{section.sectionName}</h3>

{section.fields.map((field) => {
// 3. Check conditional visibility
const isVisible = evalVisibility(field.visibility, formData, user);

if (!isVisible) return null;

// 4. Render field based on fieldType
return (
<Field
key={field.fieldId}
field={field}
value={formData[field.fieldId]}
onChange={handleFieldChange}
validation={field.validation}
error={errors[field.fieldId]}
/>
);
})}
</div>
))}

{/* Submit/Cancel buttons from pgc_config */}
<button type="submit">{pgc_config.actions.submit.label}</button>
</form>
);
};

Validation Flow

Frontend Validation (FormRenderer)

  1. Define validation rules in pgc_page_schema
  2. Apply validation per field
  3. Show error messages from validation.errorMessage
  4. Prevent submission if validation fails

Backend Validation (FormService)

// From: services/form.service.js
FormService.validatePayload = (payload, pageSchema, operation = "create") => {
const errors = {};

pageSchema.sections.forEach((section) => {
section.fields.forEach((field) => {
const value = payload[field.fieldId];

// 1. Check required fields
if (field.isRequired && !value) {
errors[field.fieldId] = "This field is required";
}

// 2. Validate length
if (value && field.validation.minLength) {
if (value.length < field.validation.minLength) {
errors[field.fieldId] =
`Must be at least ${field.validation.minLength} characters`;
}
}

// 3. Validate pattern (regex)
if (value && field.validation.pattern) {
const regex = new RegExp(field.validation.pattern);
if (!regex.test(value)) {
errors[field.fieldId] = field.validation.errorMessage;
}
}

// 4. Validate numeric range
if (field.fieldType === "number" && value) {
if (field.validation.minValue && value < field.validation.minValue) {
errors[field.fieldId] =
`Must be at least ${field.validation.minValue}`;
}
}

// 5. Check custom validation rules
if (field.validation.customRule) {
const customError = validateCustomRule(
field.validation.customRule,
value,
);
if (customError) {
errors[field.fieldId] = customError;
}
}
});
});

return { isValid: Object.keys(errors).length === 0, errors };
};

Form-to-Entity Binding

How Forms Connect to Entities

// pgc_page_schema defines entity binding:
{
"entityCode": "CUSTOMERS", // Links to app_object_registries
"profileCode": "CUSTOMERS_EDIT", // Links to app_object_profiles

"sections": [
{
"fields": [
{
"fieldId": "cus_name", // Must match registry field code
"fieldName": "Customer Name",
"fieldType": "text",

// This binds to:
// app_object_registries.obj_payload.fields[].field_code = 'cus_name'
// AND
// app_object_profiles.prf_policy.writableFields includes 'cus_name'
}
]
}
]
}

// On form submit:
// 1. Get entity registry (obj_code = 'CUSTOMERS')
// 2. Get entity profile (prf_object_code = 'CUSTOMERS', prf_type = 'edit')
// 3. Apply prf_policy.writableFields filter
// 4. Validate against prf_policy.projection
// 5. Save to app_customers table

API Examples

Get Form Configuration

GET /api/v1/page-configs/FRM_CUSTOMERS
Headers: {
'x-tenant-id': 'encrypted_value',
'Authorization': 'Bearer token'
}

Response:
{
"success": true,
"data": {
"pgc_id": 301,
"pgc_code": "FRM_CUSTOMERS",
"pgc_page_schema": { /* complete schema */ },
"pgc_config": { /* layout and actions */ }
}
}

Validate Form Payload

POST /api/v1/page-configs/FRM_CUSTOMERS/validate
Headers: {
'x-tenant-id': 'encrypted_value',
'Authorization': 'Bearer token'
}
Body: {
"cus_name": "New Customer",
"cus_email": "invalid-email", // Invalid email format
"cus_status": "ACTIVE"
}

Response:
{
"success": false,
"isValid": false,
"errors": {
"cus_email": "Valid email required"
}
}

Submit Form (Create Record)

POST /api/v1/customers
Headers: {
'x-tenant-id': 'encrypted_value',
'Authorization': 'Bearer token'
}
Body: {
"cus_name": "Acme Corp",
"cus_email": "contact@acme.com",
"cus_status": "ACTIVE"
}

Response:
{
"success": true,
"data": {
"cus_id": 123,
"cus_name": "Acme Corp",
"cus_email": "contact@acme.com",
"cus_status": "ACTIVE",
"cus_tenant_id": 1,
"cus_audit_data": {
"created_by": "user123",
"created_at": "2026-03-12T10:30:00Z"
}
}
}

Querying Page Configurations

Get All Forms for Tenant

SELECT
pgc_code,
pgc_description,
pgc_page_schema
FROM app_page_configs
WHERE pgc_tenant_id = 1
AND pgc_status = 1
AND pgc_is_deleted = 0
ORDER BY pgc_code;

Get Form Fields

SELECT
pgc_code,
JSON_QUERY(pgc_page_schema, '$.sections[*].fields[*].fieldId') as field_ids,
JSON_QUERY(pgc_page_schema, '$.sections[*].fields[*].fieldLabel') as field_labels
FROM app_page_configs
WHERE pgc_code = 'FRM_CUSTOMERS';

Caching Strategy

Page Config Caching

Cache Key: pageconfig:${tenantId}:${pageCode}
TTL: 24 hours
Example: pageconfig:1:FRM_CUSTOMERS

Invalidation Triggers:

  • Update to app_page_configs → clear pageconfig:1:FRM_CUSTOMERS
  • Automatic on INSERT/UPDATE/DELETE

Do's and Don'ts

✅ DO

  • Validate fields on both frontend and backend - Frontend for UX, backend for security
  • Use proper field types - Match fieldType to dataType (text for VARCHAR, number for INT)
  • Define error messages in validation - Users shouldn't see generic validation errors
  • Use conditional visibility - Show/hide fields based on other field values or user role
  • Group related fields in sections - Improves form UX and organization
  • Set proper field widths - Use responsive widths (50%, 100%) for multi-column layouts
  • Document field dependencies - Help future maintainers understand field relationships
  • Test forms with different user roles - Verify conditional visibility works per role

❌ DON'T

  • Hardcode validation rules in FormRenderer - Always use pgc_page_schema
  • Skip backend validation - Frontend validation can be bypassed
  • Use overly complex conditional visibility - Keep rules simple and maintainable
  • Leave required fields unmarked - Always set isRequired for mandatory fields
  • Create forms with more than 4 columns - Responsive design breaks on mobile
  • Use unclear field labels - Labels should be user-friendly, not technical
  • Forget error messages - Validation errors should explain what went wrong
  • Modify pgc_page_schema directly in code - Use configuration APIs

Common Issues & Solutions

IssueCauseSolution
Form field not renderingfieldType not supportedCheck Field type reference, use supported types
Validation not applyingRules not in pgc_page_schemaAdd validation rules to field definition
Conditional field always hiddenVisibility condition wrongDebug condition logic, test with form data
Field accepts wrong data typedataType doesn't match fieldTypeAlign dataType with fieldType
Submit button not triggeringNo submit action in pgc_configAdd submit action to actions object