26 KiB
26 KiB
Field Types System Architecture
System Overview
┌─────────────────────────────────────────────────────────────────┐
│ Frontend (Vue 3 + Nuxt) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ View Components │ │
│ ├───────────────────────────────────────────────────────────┤ │
│ │ ListView.vue │ DetailView.vue │ EditView.vue │ │
│ │ - Data Table │ - Read Display │ - Form │ │
│ │ - Search │ - Sections │ - Validation │ │
│ │ - Sort/Filter │ - Actions │ - Sections │ │
│ │ - Bulk Actions │ │ │ │
│ └────────────────────────┬──────────────────────────────────┘ │
│ │ uses │
│ ┌────────────────────────▼──────────────────────────────────┐ │
│ │ FieldRenderer.vue │ │
│ │ Universal component for rendering any field type │ │
│ │ - Handles LIST, DETAIL, EDIT modes │ │
│ │ - Type-aware rendering │ │
│ │ - Validation support │ │
│ └────────────────────────┬──────────────────────────────────┘ │
│ │ uses │
│ ┌────────────────────────▼──────────────────────────────────┐ │
│ │ shadcn-vue Components │ │
│ │ Input, Textarea, Select, Checkbox, Switch, Calendar, │ │
│ │ Table, Badge, Dialog, Popover, etc. │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ Composables │ │
│ ├───────────────────────────────────────────────────────────┤ │
│ │ useFields() │ useViewState() │ │
│ │ - Map backend data │ - CRUD operations │ │
│ │ - Build configs │ - State management │ │
│ │ - Generate sections │ - Navigation │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ Type Definitions │ │
│ │ field-types.ts - TypeScript interfaces for field system │ │
│ └───────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
│ HTTP/REST API
▼
┌─────────────────────────────────────────────────────────────────┐
│ Backend (NestJS) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ Controllers │ │
│ ├───────────────────────────────────────────────────────────┤ │
│ │ SetupObjectController │ RuntimeObjectController │ │
│ │ - GET /objects │ - GET /objects/:name │ │
│ │ - GET /objects/:name │ - GET /objects/:name/:id │ │
│ │ - GET /ui-config ✨ │ - POST /objects/:name │ │
│ │ - POST /objects │ - PUT /objects/:name/:id │ │
│ └────────────────────────┬────────────────┬─────────────────┘ │
│ │ │ │
│ ┌────────────────────────▼────────────────▼─────────────────┐ │
│ │ Services │ │
│ ├───────────────────────────────────────────────────────────┤ │
│ │ ObjectService │ FieldMapperService ✨ │ │
│ │ - CRUD operations │ - Map field definitions │ │
│ │ - Query building │ - Generate UI configs │ │
│ │ - Validation │ - Default metadata │ │
│ └────────────────────────┬──────────────────────────────────┘ │
│ │ │
│ ┌────────────────────────▼──────────────────────────────────┐ │
│ │ Models │ │
│ │ ObjectDefinition │ FieldDefinition ✨ │ │
│ │ - Object metadata │ - Field metadata │ │
│ │ │ - UIMetadata interface │ │
│ └───────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
│ Prisma/Knex
▼
┌─────────────────────────────────────────────────────────────────┐
│ Database (PostgreSQL) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ object_definitions │ │
│ │ - id, tenant_id, api_name, label, plural_label │ │
│ │ - description, is_system, table_name │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │ │
│ │ 1:many │
│ ▼ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ field_definitions │ │
│ │ - id, object_definition_id, api_name, label, type │ │
│ │ - is_required, is_unique, is_system │ │
│ │ - ui_metadata (JSONB) ✨ NEW │ │
│ │ { │ │
│ │ placeholder, helpText, showOnList, showOnDetail, │ │
│ │ showOnEdit, sortable, options, rows, min, max, │ │
│ │ validationRules, format, prefix, suffix, etc. │ │
│ │ } │ │
│ └───────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
✨ = New/Enhanced component
Data Flow
1. Loading Object Definition
┌──────────┐ GET /api/setup/objects/Contact/ui-config ┌──────────┐
│ │ ──────────────────────────────────────────────────> │ │
│ Frontend │ │ Backend │
│ │ <────────────────────────────────────────────────── │ │
└──────────┘ { objectDef with mapped fields } └──────────┘
│
│ useFields().buildListViewConfig(objectDef)
▼
┌──────────────────────────────────────┐
│ ListViewConfig │
│ - objectApiName: "Contact" │
│ - mode: "list" │
│ - fields: [ │
│ { │
│ apiName: "firstName", │
│ type: "text", │
│ showOnList: true, │
│ ... │
│ } │
│ ] │
└──────────────────────────────────────┘
│
│ Pass to ListView component
▼
┌──────────────────────────────────────┐
│ ListView renders data table │
└──────────────────────────────────────┘
2. Fetching Records
┌──────────┐ GET /api/runtime/objects/Contact ┌──────────┐
│ │ ──────────────────────────────────────────────────> │ │
│ Frontend │ │ Backend │
│ │ <────────────────────────────────────────────────── │ │
└──────────┘ [{ id, firstName, lastName, ... }] └──────────┘
│
▼
┌──────────────────────────────────────┐
│ ListView displays records │
│ Each field rendered by │
│ FieldRenderer with mode="list" │
└──────────────────────────────────────┘
3. Field Rendering
┌─────────────────────────────────────────────────────────────┐
│ FieldRenderer │
│ Props: { field, modelValue, mode } │
└────────────────────────┬────────────────────────────────────┘
│
┌────────────────┼────────────────┐
│ │ │
▼ ▼ ▼
mode="list" mode="detail" mode="edit"
│ │ │
▼ ▼ ▼
Simple text Formatted Input component
or badge display with based on type:
display labels - Input
- Textarea
- Select
- DatePicker
- Checkbox
- etc.
4. Saving Record
┌──────────┐ ┌──────────┐
│ EditView │ ──> User fills form ──> Validation │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ Valid? │ │
│ │ ✓ Yes │ │
│ │ @save event │ │ │
│ │ ──────────────────────────┘ │ │
│ │ │ │
│ │ POST/PUT /api/runtime/objects/Contact/:id │ Backend │
│ Frontend │ ──────────────────────────────────────────────────> │ │
│ │ │ │
│ │ <────────────────────────────────────────────────── │ │
│ │ { saved record } │ │
│ │ │ │
│ │ ──> Navigate to DetailView │ │
└──────────┘ └──────────┘
Component Hierarchy
Page/App
└── ObjectViewContainer
├── ListView
│ ├── Search/Filters
│ ├── Table
│ │ ├── TableHeader
│ │ │ └── Sortable columns
│ │ └── TableBody
│ │ └── TableRow (for each record)
│ │ └── TableCell (for each field)
│ │ └── FieldRenderer (mode="list")
│ └── Actions (Create, Export, etc.)
│
├── DetailView
│ ├── Header with actions
│ └── Sections
│ └── Card (for each section)
│ └── FieldRenderer (mode="detail") for each field
│
└── EditView
├── Header with Save/Cancel
└── Form
└── Sections
└── Card (for each section)
└── FieldRenderer (mode="edit") for each field
└── Input component based on field type
Field Type Mapping
Database Type → FieldType Enum → Component (Edit Mode)
─────────────────────────────────────────────────────────
string → TEXT → Input[type="text"]
text → TEXTAREA → Textarea
email → EMAIL → Input[type="email"]
url → URL → Input[type="url"]
integer → NUMBER → Input[type="number"]
decimal → NUMBER → Input[type="number"]
currency → CURRENCY → Input[type="number"] + prefix
boolean → BOOLEAN → Checkbox
date → DATE → DatePicker
datetime → DATETIME → DatePicker (with time)
picklist → SELECT → Select
multipicklist → MULTI_SELECT → Select[multiple]
lookup → BELONGS_TO → Combobox (relation picker)
file → FILE → FileUpload
image → IMAGE → ImageUpload
richtext → MARKDOWN → Textarea (+ preview)
json → JSON → Textarea (JSON editor)
View Mode Rendering
Field Type: TEXT
─────────────────────────────────────────────────────
LIST mode │ Simple text, truncated
│ <span>{{ value }}</span>
─────────────────────────────────────────────────────
DETAIL mode │ Text with label
│ <div>
│ <Label>Name</Label>
│ <span>{{ value }}</span>
│ </div>
─────────────────────────────────────────────────────
EDIT mode │ Input field
│ <Input v-model="value" />
─────────────────────────────────────────────────────
Field Type: BOOLEAN
─────────────────────────────────────────────────────
LIST mode │ Badge (Yes/No)
│ <Badge>Yes</Badge>
─────────────────────────────────────────────────────
DETAIL mode │ Checkbox (disabled) + text
│ <Checkbox :checked="value" disabled />
│ <span>Yes</span>
─────────────────────────────────────────────────────
EDIT mode │ Checkbox (editable)
│ <Checkbox v-model="value" />
─────────────────────────────────────────────────────
Field Type: SELECT
─────────────────────────────────────────────────────
LIST mode │ Selected label
│ <span>Active</span>
─────────────────────────────────────────────────────
DETAIL mode │ Selected label with styling
│ <Badge>Active</Badge>
─────────────────────────────────────────────────────
EDIT mode │ Dropdown select
│ <Select v-model="value">
│ <SelectItem value="active">Active</SelectItem>
│ </Select>
─────────────────────────────────────────────────────
API Endpoints
Setup/Configuration (Metadata)
────────────────────────────────────────────────────
GET /api/setup/objects
Returns: List of all object definitions
GET /api/setup/objects/:objectName
Returns: Object definition with fields
GET /api/setup/objects/:objectName/ui-config ✨
Returns: Object definition with UI-ready field configs
(fields mapped to frontend format with UIMetadata)
POST /api/setup/objects
Body: { apiName, label, description, ... }
Returns: Created object definition
POST /api/setup/objects/:objectName/fields
Body: { apiName, label, type, uiMetadata, ... }
Returns: Created field definition
Runtime (Data CRUD)
────────────────────────────────────────────────────
GET /api/runtime/objects/:objectName
Query: { search, filters, page, pageSize }
Returns: Array of records
GET /api/runtime/objects/:objectName/:recordId
Returns: Single record
POST /api/runtime/objects/:objectName
Body: { field1: value1, field2: value2, ... }
Returns: Created record
PUT /api/runtime/objects/:objectName/:recordId
Body: { field1: value1, field2: value2, ... }
Returns: Updated record
DELETE /api/runtime/objects/:objectName/:recordId
Returns: Success status
Key Features
Frontend
- ✅ Universal field renderer for 15+ field types
- ✅ Three view modes (list, detail, edit)
- ✅ Client-side validation with custom rules
- ✅ Responsive design (mobile-friendly)
- ✅ Accessible components (WCAG compliant)
- ✅ Type-safe with TypeScript
- ✅ Composables for easy integration
- ✅ Demo page for testing
Backend
- ✅ UI metadata stored in JSONB column
- ✅ Field mapper service for transformation
- ✅ Default metadata generation
- ✅ Validation rule support
- ✅ Flexible field type system
- ✅ Multi-tenant support
- ✅ RESTful API
Database
- ✅ Flexible schema with JSONB metadata
- ✅ Support for custom objects
- ✅ Versioning and migration support
- ✅ Indexed for performance
Extension Points
1. Custom Field Types
└─> Add to FieldType enum
└─> Add rendering logic to FieldRenderer.vue
└─> Add mapping in FieldMapperService
2. Custom Validation Rules
└─> Add to ValidationRule type
└─> Add validation logic in EditView.vue
3. Custom Actions
└─> Add to ViewAction interface
└─> Handle in view components
4. Custom Sections
└─> Configure in DetailViewConfig/EditViewConfig
└─> Auto-generation in useFields()
5. Custom Formatting
└─> Add to UIMetadata
└─> Implement in FieldRenderer.vue
This architecture provides a scalable, maintainable, and extensible system for building dynamic forms and views! 🎉