11 KiB
11 KiB
Field Types & Views System
A comprehensive field type system inspired by Laravel Nova, built with Vue 3 and shadcn-vue components. This system provides a flexible way to define and render fields in list, detail, and edit views.
Overview
The system consists of:
- Field Type Definitions - TypeScript types and enums defining all available field types
- Field Renderer - A universal component that renders fields based on type and view mode
- View Components - ListView (data table), DetailView, and EditView components
- Composables - Utilities for working with fields and managing CRUD operations
- Backend Support - Extended field definitions with UI metadata
Field Types
Text Fields
TEXT- Single-line text inputTEXTAREA- Multi-line text inputPASSWORD- Password input (masked)EMAIL- Email input with validationURL- URL input
Numeric Fields
NUMBER- Numeric inputCURRENCY- Currency input with formatting
Selection Fields
SELECT- Dropdown selectMULTI_SELECT- Multi-select dropdownBOOLEAN- Checkbox/switch
Date/Time Fields
DATE- Date pickerDATETIME- Date and time pickerTIME- Time picker
Relationship Fields
BELONGS_TO- Many-to-one relationshipHAS_MANY- One-to-many relationshipMANY_TO_MANY- Many-to-many relationship
Rich Content
MARKDOWN- Markdown editorCODE- Code editor
File Fields
FILE- File uploadIMAGE- Image upload
Other
COLOR- Color pickerJSON- JSON editor
Usage
Basic Example
<script setup lang="ts">
import { ListView, DetailView, EditView } from '@/components/views'
import { FieldType, ViewMode } from '@/types/field-types'
// Define your fields
const fields = [
{
id: '1',
apiName: 'name',
label: 'Name',
type: FieldType.TEXT,
isRequired: true,
placeholder: 'Enter name',
showOnList: true,
showOnDetail: true,
showOnEdit: true,
},
{
id: '2',
apiName: 'email',
label: 'Email',
type: FieldType.EMAIL,
isRequired: true,
validationRules: [
{ type: 'email', message: 'Invalid email format' }
],
},
{
id: '3',
apiName: 'status',
label: 'Status',
type: FieldType.SELECT,
options: [
{ label: 'Active', value: 'active' },
{ label: 'Inactive', value: 'inactive' },
],
},
]
// Create view config
const listConfig = {
objectApiName: 'Contact',
mode: ViewMode.LIST,
fields,
searchable: true,
exportable: true,
}
const data = ref([])
</script>
<template>
<ListView
:config="listConfig"
:data="data"
selectable
@row-click="handleRowClick"
@create="handleCreate"
/>
</template>
Using with Backend Data
<script setup lang="ts">
import { useFields, useViewState } from '@/composables/useFieldViews'
import { ListView } from '@/components/views'
const { buildListViewConfig } = useFields()
const {
records,
loading,
fetchRecords,
showDetail,
showEdit,
deleteRecords
} = useViewState('/api/contacts')
// Fetch object definition from backend
const objectDef = await $fetch('/api/objects/contact')
// Build view config from backend data
const listConfig = buildListViewConfig(objectDef, {
searchable: true,
exportable: true,
})
// Fetch records
await fetchRecords()
</script>
<template>
<ListView
:config="listConfig"
:data="records"
:loading="loading"
@row-click="showDetail"
@create="showEdit"
@delete="deleteRecords"
/>
</template>
Sections and Grouping
const detailConfig = {
objectApiName: 'Contact',
mode: ViewMode.DETAIL,
fields,
sections: [
{
title: 'Basic Information',
description: 'Primary contact details',
fields: ['firstName', 'lastName', 'email'],
},
{
title: 'Company Information',
fields: ['company', 'jobTitle', 'department'],
},
{
title: 'Additional Details',
fields: ['notes', 'tags'],
collapsible: true,
defaultCollapsed: true,
},
],
}
Field Configuration
FieldConfig Interface
interface FieldConfig {
// Basic properties
id: string
apiName: string
label: string
type: FieldType
// Display
placeholder?: string
helpText?: string
defaultValue?: any
// Validation
isRequired?: boolean
isReadOnly?: boolean
validationRules?: FieldValidationRule[]
// View visibility
showOnList?: boolean
showOnDetail?: boolean
showOnEdit?: boolean
sortable?: boolean
// Type-specific options
options?: FieldOption[] // For select fields
rows?: number // For textarea
min?: number // For number/date
max?: number // For number/date
step?: number // For number
accept?: string // For file uploads
relationObject?: string // For relationships
// Formatting
format?: string
prefix?: string
suffix?: string
}
Validation Rules
const field = {
// ... other config
validationRules: [
{ type: 'required', message: 'This field is required' },
{ type: 'min', value: 5, message: 'Minimum 5 characters' },
{ type: 'max', value: 100, message: 'Maximum 100 characters' },
{ type: 'email', message: 'Invalid email format' },
{ type: 'url', message: 'Invalid URL format' },
{ type: 'pattern', value: '^[A-Z]', message: 'Must start with uppercase' },
],
}
View Components
ListView
Features:
- Data table with sortable columns
- Row selection with bulk actions
- Search functionality
- Custom actions
- Export capability
- Pagination support
Events:
row-click- When a row is clickedrow-select- When rows are selectedcreate- When create button is clickededit- When edit button is clickeddelete- When delete is triggeredaction- When custom action is triggeredsort- When column sort changessearch- When search is performed
DetailView
Features:
- Organized sections
- Collapsible sections
- Custom actions
- Read-only display optimized for each field type
Events:
edit- When edit button is clickeddelete- When delete button is clickedback- When back button is clickedaction- When custom action is triggered
EditView
Features:
- Form with validation
- Organized sections with collapsible support
- Required field indicators
- Help text and placeholders
- Error messages
- Save/Cancel actions
Events:
save- When form is submitted (passes validated data)cancel- When cancel is clickedback- When back is clicked
Backend Integration
Field Definition Model
export interface UIMetadata {
placeholder?: string
helpText?: string
showOnList?: boolean
showOnDetail?: boolean
showOnEdit?: boolean
sortable?: boolean
options?: FieldOption[]
rows?: number
min?: number
max?: number
step?: number
format?: string
prefix?: string
suffix?: string
validationRules?: ValidationRule[]
}
export class FieldDefinition extends BaseModel {
// ... existing fields
uiMetadata?: UIMetadata
}
Migration
Run the migration to add UI metadata support:
cd backend
npm run migrate:tenant
API Response Example
{
"id": "field-1",
"objectDefinitionId": "obj-1",
"apiName": "firstName",
"label": "First Name",
"type": "text",
"isRequired": true,
"uiMetadata": {
"placeholder": "Enter first name",
"helpText": "Customer's legal first name",
"showOnList": true,
"showOnDetail": true,
"showOnEdit": true,
"sortable": true,
"validationRules": [
{
"type": "min",
"value": 2,
"message": "Name must be at least 2 characters"
}
]
}
}
Composables
useFields()
Utilities for working with field configurations:
mapFieldDefinitionToConfig(fieldDef)- Convert backend field definition to FieldConfigbuildListViewConfig(objectDef, customConfig)- Build ListView configurationbuildDetailViewConfig(objectDef, customConfig)- Build DetailView configurationbuildEditViewConfig(objectDef, customConfig)- Build EditView configurationgenerateSections(fields)- Auto-generate sections based on field types
useViewState(apiEndpoint)
CRUD operations and state management:
- State:
records,currentRecord,currentView,loading,saving,error - Methods:
fetchRecords(),fetchRecord(id),createRecord(data),updateRecord(id, data),deleteRecord(id),deleteRecords(ids) - Navigation:
showList(),showDetail(record),showEdit(record),handleSave(data)
Demo
Visit /demo/field-views to see an interactive demo of all field types and views.
Best Practices
- Field Organization - Group related fields into sections for better UX
- Validation - Always provide clear validation messages
- Help Text - Use help text to guide users
- Required Fields - Mark required fields appropriately
- Default Values - Provide sensible defaults when possible
- Read-Only Fields - Use for system fields or computed values
- Conditional Logic - Use
dependsOnfor conditional field visibility - Mobile Responsive - All components are mobile-responsive by default
Extending
Adding Custom Field Types
- Add new type to
FieldTypeenum in types/field-types.ts - Add rendering logic to FieldRenderer.vue
- Update validation logic in EditView.vue
Custom Actions
const config = {
// ... other config
actions: [
{
id: 'export-pdf',
label: 'Export PDF',
icon: 'FileDown',
variant: 'outline',
confirmation: 'Export this record to PDF?',
handler: async () => {
// Custom logic
}
}
]
}
Components Structure
frontend/
├── components/
│ ├── fields/
│ │ └── FieldRenderer.vue # Universal field renderer
│ ├── views/
│ │ ├── ListView.vue # Data table view
│ │ ├── DetailView.vue # Read-only detail view
│ │ └── EditView.vue # Form/edit view
│ └── ui/ # shadcn-vue components
│ ├── table/
│ ├── input/
│ ├── select/
│ ├── checkbox/
│ ├── switch/
│ ├── textarea/
│ ├── calendar/
│ ├── date-picker/
│ └── ...
├── types/
│ └── field-types.ts # Type definitions
├── composables/
│ └── useFieldViews.ts # Utilities
└── pages/
└── demo/
└── field-views.vue # Interactive demo
Performance Considerations
- Fields are rendered on-demand based on view mode
- Large datasets should use pagination (built-in support)
- Validation is performed client-side before API calls
- Use
v-memofor large lists to optimize re-renders
Accessibility
All components follow accessibility best practices:
- Proper ARIA labels
- Keyboard navigation support
- Focus management
- Screen reader friendly
- High contrast support
License
Part of the Neo platform.