Skip to main content

DataView Configuration Documentation

Module: Data Display Configuration
Version: 1.0
Last Updated: March 12, 2026


📌 Overview

DataView Configuration manages data display components in the frontend. It defines:

  • Data grid columns: field mapping, sorting, filtering
  • Data display formats: column types, custom renderers
  • User interactions: inline editing, column visibility, export, grouping
  • Performance optimization: lazy loading, pagination, virtual scrolling

Core Table:

  • app_dataview_configs - DataView definitions

Related:

  • app_object_registries - Source entity definitions
  • app_object_profiles - Field-level masking for display

app_dataview_configs - DataView Definitions

Purpose

Stores complete data grid/list view configurations including column definitions, sorting, filtering, and display options.

Schema

SELECT
dvc_id (INT), -- Primary Key
dvc_tenant_id (INT), -- Tenant isolation
dvc_code (VARCHAR(256)), -- Identifier (e.g., 'CUSTOMERS_LIST')
dvc_description (VARCHAR), -- Display name
dvc_source_entity (VARCHAR), -- Links to app_object_registries
dvc_config (JSON), -- Complete dataview definition
dvc_page_config_id (INT), -- Parent page (if part of form)
dvc_is_default (BIT), -- 1=Default view, 0=Custom
dvc_status (INT), -- 1=Active, 0=Inactive
dvc_is_deleted (BIT), -- Soft delete flag
dvc_audit_data (JSON), -- Audit trail
dvc_add_dtls (JSON) -- Additional metadata
FROM app_dataview_configs
WHERE dvc_status = 1 AND dvc_is_deleted = 0

dvc_config Structure (JSON) - Complete DataView Definition

{
"dataviewId": "DVC_CUSTOMERS_LIST",
"dataviewName": "Customers List",
"dataviewDescription": "All registered customers with status and details",

"source": {
"entityCode": "CUSTOMERS",
"primaryKey": "customer_id",
"maxRows": 10000
},

"display": {
"pageSize": 25, // Rows per page
"paginationType": "offset", // 'offset', 'cursor', 'virtual'
"enableVirtualScrolling": true, // For large datasets
"enableColumnResize": true,
"enableRowSelection": true,
"selectionMode": "multiple", // 'single', 'multiple', 'none'
"enableToolbar": true,
"toolbarPosition": "top" // 'top', 'bottom', 'both'
},

"columns": [
{
"columnId": "col_customer_id",
"fieldCode": "customer_id", // Maps to CUSTOMERS entity field
"columnName": "ID",
"columnOrder": 1,
"columnWidth": 80,
"columnType": "numeric", // 'text', 'numeric', 'date', 'boolean', 'custom'
"isVisible": true,
"isSortable": true,
"isFilterable": true,
"isFrozen": true, // Sticky column
"align": "left",
"format": null
},
{
"columnId": "col_customer_name",
"fieldCode": "name",
"columnName": "Customer Name",
"columnOrder": 2,
"columnWidth": 200,
"columnType": "text",
"isVisible": true,
"isSortable": true,
"isFilterable": true,
"isFrozen": false,
"align": "left",
"format": null,
"searchable": true
},
{
"columnId": "col_customer_email",
"fieldCode": "email",
"columnName": "Email",
"columnOrder": 3,
"columnWidth": 220,
"columnType": "text",
"isVisible": true,
"isSortable": false,
"isFilterable": true,
"renderType": "email", // Special email renderer
"align": "left"
},
{
"columnId": "col_customer_phone",
"fieldCode": "phone",
"columnName": "Phone",
"columnOrder": 4,
"columnWidth": 150,
"columnType": "text",
"isVisible": false, // Hidden by default
"isSortable": false,
"isFilterable": true,
"renderType": "phone"
},
{
"columnId": "col_customer_status",
"fieldCode": "status",
"columnName": "Status",
"columnOrder": 5,
"columnWidth": 120,
"columnType": "select",
"isVisible": true,
"isSortable": true,
"isFilterable": true,
"filterOptions": [
{ "value": "ACTIVE", "label": "Active" },
{ "value": "INACTIVE", "label": "Inactive" },
{ "value": "SUSPENDED", "label": "Suspended" }
],
"cellRenderer": {
"type": "badge",
"colors": {
"ACTIVE": "success",
"INACTIVE": "secondary",
"SUSPENDED": "warning"
}
}
},
{
"columnId": "col_registration_date",
"fieldCode": "created_at",
"columnName": "Registered",
"columnOrder": 6,
"columnWidth": 140,
"columnType": "date",
"isVisible": true,
"isSortable": true,
"isFilterable": true,
"format": "MM/DD/YYYY"
},
{
"columnId": "col_total_orders",
"fieldCode": "total_orders",
"columnName": "Total Orders",
"columnOrder": 7,
"columnWidth": 130,
"columnType": "numeric",
"isVisible": true,
"isSortable": true,
"isFilterable": false,
"aggregateFunction": "sum", // For footers
"align": "right"
},
{
"columnId": "col_actions",
"fieldCode": null, // Synthetic column
"columnName": "Actions",
"columnOrder": 8,
"columnWidth": 100,
"columnType": "actions",
"isSortable": false,
"isFilterable": false,
"isFrozen": true,
"actions": [
{
"actionId": "act_edit",
"actionName": "Edit",
"icon": "edit",
"onClick": "openEditForm"
},
{
"actionId": "act_delete",
"actionName": "Delete",
"icon": "trash",
"onClick": "deleteRecord",
"confirmMessage": "Are you sure?"
}
]
}
],

"filters": {
"enableFiltering": true,
"filterLayout": "toolbar", // 'toolbar', 'sidebar', 'both'
"defaultFilters": [
{
"filterId": "filter_active",
"fieldCode": "status",
"operator": "equals",
"value": "ACTIVE"
}
],
"filterOptions": [
{
"filterId": "filter_status",
"fieldCode": "status",
"label": "Status",
"operators": ["equals", "not_equals", "in"],
"type": "select"
},
{
"filterId": "filter_date_range",
"fieldCode": "created_at",
"label": "Registration Date",
"operators": ["between", "after", "before"],
"type": "daterange"
},
{
"filterId": "filter_search",
"fieldCode": "name",
"label": "Search Name",
"operators": ["contains", "starts_with"],
"type": "text"
}
]
},

"sorting": {
"enableSorting": true,
"defaultSort": [
{
"fieldCode": "created_at",
"direction": "desc"
}
],
"allowMultiSort": true
},

"grouping": {
"enableGrouping": true,
"defaultGroupBy": null,
"groupableColumns": ["status", "created_at"]
},

"inlineEditing": {
"enableInlineEdit": false, // Edit in place
"editableColumns": ["name", "email", "phone"],
"saveOnBlur": true
},

"actions": {
"enableRowActions": true,
"enableBulkActions": true,
"bulkActions": [
{
"actionId": "bulk_delete",
"actionName": "Delete Selected",
"icon": "trash",
"requireConfirm": true
},
{
"actionId": "bulk_export",
"actionName": "Export Selected",
"icon": "download"
},
{
"actionId": "bulk_status",
"actionName": "Change Status",
"icon": "check"
}
]
},

"export": {
"enableExport": true,
"exportFormats": ["csv", "excel", "pdf"],
"exportFilename": "customers_${date}.xlsx"
}
}

Column Type Reference

TypePurposeRenderer Examples
textText displayplain, email, phone, url, custom
numericNumbers with formattinginteger, decimal, currency, percentage
dateDate/time displaydate, time, datetime, relative
booleanTrue/False displaycheckbox, toggle, badge
selectDropdown optionsbadge, dropdown, tag
customCustom HTML rendereravatar, status indicator, progress bar
actionsButtons/menusaction buttons, dropdown menu
linkHyperlinklink to detail form, route navigation

DataView Filtering

Operator Reference

OperatorData TypeDescription
equalsAllField value equals
not_equalsAllField value not equals
inAllField value in list
not_inAllField value not in list
containsTextText contains substring
starts_withTextText starts with
ends_withTextText ends with
greater_thanNumericNumber > value
less_thanNumericNumber < value
betweenNumeric/DateValue between range
is_nullAllField is NULL
is_not_nullAllField is NOT NULL

Frontend DataView Component Integration

DataViewRenderer Component

// From: axiom-genesis-frontend/src/components/renderers/DataViewRenderer.tsx
interface DataViewRendererProps {
dataviewCode: string; // Links to app_dataview_configs.dvc_code
recordData?: Record<string, any>; // Parent record context
onRowSelect?: (rows: any[]) => void;
onAction?: (actionId: string, record: any) => void;
height?: number;
}

export const DataViewRenderer: React.FC<DataViewRendererProps> = ({
dataviewCode,
recordData,
onRowSelect,
onAction,
height = 400
}) => {
const [config, setConfig] = useState<any>(null);
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
const [filters, setFilters] = useState(config?.filters?.defaultFilters || []);
const [page, setPage] = useState(0);
const [pageSize, setPageSize] = useState(config?.display?.pageSize || 25);

// 1. Load dataview config
useEffect(() => {
const fetchConfig = async () => {
const response = await api.dataview.getConfig(dataviewCode);
setConfig(response.data);
};
fetchConfig();
}, [dataviewCode]);

// 2. Load data (respects filters, sorting, pagination)
useEffect(() => {
if (!config) return;

const fetchData = async () => {
setLoading(true);
const response = await api.dataview.getData(dataviewCode, {
filters,
page,
pageSize,
sort: config.sorting?.defaultSort
});
setData(response.data);
setLoading(false);
};

fetchData();
}, [config, filters, page, pageSize]);

return (
<div className="dataview-container">
{/* Filtering toolbar */}
{config?.filters?.enableFiltering && (
<FilterBar
filters={config.filters.filterOptions}
onApply={(newFilters) => {
setFilters(newFilters);
setPage(0); // Reset pagination
}}
/>
)}

{/* Data grid */}
<DataGrid
columns={config?.columns}
rows={data}
loading={loading}
pageSize={pageSize}
onPageSizeChange={setPageSize}
onRowSelect={onRowSelect}
onAction={(actionId, record) => {
if (onAction) onAction(actionId, record);
else handleDefaultAction(actionId, record);
}}
height={height}
/>

{/* Bulk actions toolbar */}
{config?.actions?.enableBulkActions && (
<BulkActionsBar
selectedCount={selectedRows.length}
actions={config.actions.bulkActions}
onAction={(actionId) => handleBulkAction(actionId)}
/>
)}
</div>
);
};

Usage in Parent Forms

// In a parent form showing related records
<FormSection title="Customer Orders">
<DataViewRenderer
dataviewCode="CUSTOMERS_ORDERS_LIST"
recordData={customerData}
onRowSelect={(orders) => setSelectedOrders(orders)}
onAction={(actionId, order) => {
if (actionId === 'edit') openOrderDetail(order.order_id);
if (actionId === 'delete') deleteOrder(order.order_id);
}}
height={600}
/>
</FormSection>

Backend DataView Service

Data Retrieval Service

// From: axiom-genesis-middleware/services/dataview.service.js
DataViewService.getConfigById = async (dvConfigId, tenantId) => {
const db = getConnection();

const result = await db
.request()
.input("dvc_id", dvConfigId)
.input("dvc_tenant_id", tenantId).query(`
SELECT dvc_config, dvc_source_entity
FROM app_dataview_configs
WHERE dvc_id = @dvc_id AND dvc_tenant_id = @dvc_tenant_id
`);

return {
id: result.recordset[0].dvc_id,
config: JSON.parse(result.recordset[0].dvc_config),
sourceEntity: result.recordset[0].dvc_source_entity,
};
};

DataViewService.getData = async (
dvConfigId,
filters,
sorting,
page,
pageSize,
tenantId,
) => {
const config = await this.getConfigById(dvConfigId, tenantId);
const entity = config.sourceEntity;

// Build dynamic SQL based on config
let query = `SELECT ${config.config.columns
.filter((c) => c.fieldCode)
.map((c) => c.fieldCode)
.join(", ")}
FROM ${entity}
WHERE 1=1`;

const request = new sql.Request();

// Apply default filters
const allFilters = [
...(config.config.filters?.defaultFilters || []),
...(filters || []),
];

for (const filter of allFilters) {
const targetColumn = filter.fieldCode;

switch (filter.operator) {
case "equals":
query += ` AND ${targetColumn} = @filter_${filter.filterId}`;
request.input(`filter_${filter.filterId}`, filter.value);
break;
case "contains":
query += ` AND ${targetColumn} LIKE @filter_${filter.filterId}`;
request.input(`filter_${filter.filterId}`, `%${filter.value}%`);
break;
case "between":
query += ` AND ${targetColumn} BETWEEN @filter_from AND @filter_to`;
request.input("filter_from", filter.value[0]);
request.input("filter_to", filter.value[1]);
break;
// Handle other operators
}
}

// Apply sorting
const sortConfig = sorting || config.config.sorting?.defaultSort;
if (sortConfig && sortConfig.length > 0) {
const sortClause = sortConfig
.map((s) => `${s.fieldCode} ${s.direction === "desc" ? "DESC" : "ASC"}`)
.join(", ");
query += ` ORDER BY ${sortClause}`;
}

// Apply pagination (OFFSET/FETCH)
const offset = (page || 0) * pageSize;
query += ` OFFSET ${offset} ROWS FETCH NEXT ${pageSize} ROWS ONLY`;

// Update request with tenant isolation
request.input("tenant_id", tenantId);
query = query.replace(
/FROM.*WHERE/i,
`FROM ${entity} WHERE tenant_id = @tenant_id AND`,
);

const result = await request.query(query);
return result.recordset;
};

DataViewService.exportData = async (dvConfigId, format, filters, tenantId) => {
const data = await this.getData(
dvConfigId,
filters,
null,
0,
10000,
tenantId,
);
const config = await this.getConfigById(dvConfigId, tenantId);

switch (format) {
case "csv":
return ExportService.toCSV(data, config.config.columns);
case "excel":
return ExportService.toExcel(data, config.config.columns);
case "pdf":
return ExportService.toPDF(data, config.config.columns);
}
};

Caching DataView Configs

// Cache strategy for better performance
const cacheDataViewConfig = async (dvConfigId, tenantId) => {
const cacheKey = `dataview:${tenantId}:${dvConfigId}`;
const cached = await redis.get(cacheKey);

if (cached) {
return JSON.parse(cached);
}

const config = await db
.request()
.input("dvc_id", dvConfigId)
.input("dvc_tenant_id", tenantId)
.query(`SELECT dvc_config FROM app_dataview_configs WHERE ...`);

// Cache for 1 hour
await redis.setex(cacheKey, 3600, JSON.stringify(config));

return config;
};

Queries for DataView Configs

Get All DataViews for Entity

SELECT
dvc_code,
dvc_description,
dvc_source_entity,
dvc_is_default
FROM app_dataview_configs
WHERE dvc_source_entity = 'CUSTOMERS'
AND dvc_tenant_id = 1
AND dvc_status = 1
ORDER BY dvc_is_default DESC, dvc_code;

Get DataView with Column Info

SELECT
dvc_code,
dvc_description,
JSON_EXTRACT(dvc_config, '$.columns') as columns,
JSON_EXTRACT(dvc_config, '$.filters') as filters,
JSON_EXTRACT(dvc_config, '$.sorting') as sorting
FROM app_dataview_configs
WHERE dvc_code = 'CUSTOMERS_LIST'
AND dvc_tenant_id = 1;

API Examples

Get DataView Config

GET /api/v1/dataviews/CUSTOMERS_LIST/config
Headers: {
'x-tenant-id': 'encrypted_value',
'Authorization': 'Bearer token'
}

Response:
{
"success": true,
"data": {
"dvc_code": "CUSTOMERS_LIST",
"dvc_description": "Customers List",
"config": {
"source": { "entityCode": "CUSTOMERS" },
"columns": [...],
"filters": {...},
"display": {...}
}
}
}

Get DataView Data with Filters

GET /api/v1/dataviews/CUSTOMERS_LIST/data?
page=0&pageSize=25&
filters=[{"fieldCode":"status","operator":"equals","value":"ACTIVE"}]
Headers: {
'x-tenant-id': 'encrypted_value',
'Authorization': 'Bearer token'
}

Response:
{
"success": true,
"data": [
{
"customer_id": 1,
"name": "John Doe",
"email": "john@example.com",
"status": "ACTIVE",
"created_at": "2026-03-01"
},
...
],
"pagination": {
"page": 0,
"pageSize": 25,
"total": 150
}
}

Export DataView Data

POST /api/v1/dataviews/CUSTOMERS_LIST/export
Headers: {
'x-tenant-id': 'encrypted_value',
'Authorization': 'Bearer token'
}
Body: {
"format": "excel",
"filters": [{"fieldCode":"status","operator":"equals","value":"ACTIVE"}]
}

Response:
{
"success": true,
"data": {
"filename": "customers_2026-03-12.xlsx",
"downloadUrl": "/api/v1/downloads/file_uuid"
}
}

Do's and Don'ts

✅ DO

  • Define meaningful column names - Users need clear headers
  • Use appropriate column types - Ensures proper sorting and filtering
  • Implement pagination - Large datasets need pagination for performance
  • Freeze critical columns - Keep ID and Name visible when scrolling
  • Sort by most useful field by default - Usually date descending
  • Use badge/status renderers - Visual indicators for status fields
  • Enable bulk actions - Users expect to act on multiple records
  • Cache dataview configs - Reduces database queries
  • Respect field masking - Apply app_object_profiles rules to display

❌ DON'T

  • Display all columns by default - Hide non-essential fields
  • Use generic column names - e.g., "Field1", "Field2"
  • Load all records at once - Implement pagination/virtual scrolling
  • Make non-sortable columns sortable - Confuses users
  • Forget to validate filter values - Prevent SQL injection
  • Bypass masking rules - Always apply field-level access control
  • Mix entity codes - Don't reference fields from wrong entities
  • Load configs on every request - Cache aggressively with invalidation