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
| Type | Data types | Validation | Component |
|---|---|---|---|
text | VARCHAR, CHAR | min/max length, pattern | Text input |
email | VARCHAR | email pattern | Email input |
number | INT, DECIMAL, FLOAT | min/max value | Number input |
date | DATE, DATETIME | date range | Date picker |
select | VARCHAR, INT | options, required | Dropdown/select |
checkbox | BIT | true/false | Checkbox |
radio | VARCHAR | options | Radio buttons |
textarea | VARCHAR(MAX), TEXT | max length | Text area |
file | VARBINARY, FILE_ID | file type, max size | File upload |
password | VARCHAR | min length, strength | Password input |
json | JSON | JSON structure | JSON 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)
- Define validation rules in pgc_page_schema
- Apply validation per field
- Show error messages from validation.errorMessage
- 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
| Issue | Cause | Solution |
|---|---|---|
| Form field not rendering | fieldType not supported | Check Field type reference, use supported types |
| Validation not applying | Rules not in pgc_page_schema | Add validation rules to field definition |
| Conditional field always hidden | Visibility condition wrong | Debug condition logic, test with form data |
| Field accepts wrong data type | dataType doesn't match fieldType | Align dataType with fieldType |
| Submit button not triggering | No submit action in pgc_config | Add submit action to actions object |
Related Documentation
- OBJECT_MANAGEMENT.md - How forms link to entities
- DATABASE.md - Core schema reference
- PAGE.md - Page designer guide