Added auth functionality, initial work with views and field types
This commit is contained in:
295
backend/src/object/field-mapper.service.ts
Normal file
295
backend/src/object/field-mapper.service.ts
Normal file
@@ -0,0 +1,295 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { FieldDefinition } from '../models/field-definition.model';
|
||||
|
||||
export interface FieldConfigDTO {
|
||||
id: string;
|
||||
apiName: string;
|
||||
label: string;
|
||||
type: string;
|
||||
placeholder?: string;
|
||||
helpText?: string;
|
||||
defaultValue?: any;
|
||||
isRequired?: boolean;
|
||||
isReadOnly?: boolean;
|
||||
showOnList?: boolean;
|
||||
showOnDetail?: boolean;
|
||||
showOnEdit?: boolean;
|
||||
sortable?: boolean;
|
||||
options?: Array<{ label: string; value: any }>;
|
||||
rows?: number;
|
||||
min?: number;
|
||||
max?: number;
|
||||
step?: number;
|
||||
accept?: string;
|
||||
relationObject?: string;
|
||||
relationDisplayField?: string;
|
||||
format?: string;
|
||||
prefix?: string;
|
||||
suffix?: string;
|
||||
validationRules?: Array<{
|
||||
type: string;
|
||||
value?: any;
|
||||
message?: string;
|
||||
}>;
|
||||
dependsOn?: string[];
|
||||
computedValue?: string;
|
||||
}
|
||||
|
||||
export interface ObjectDefinitionDTO {
|
||||
id: string;
|
||||
apiName: string;
|
||||
label: string;
|
||||
pluralLabel?: string;
|
||||
description?: string;
|
||||
isSystem: boolean;
|
||||
fields: FieldConfigDTO[];
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class FieldMapperService {
|
||||
/**
|
||||
* Convert a field definition from the database to a frontend-friendly FieldConfig
|
||||
*/
|
||||
mapFieldToDTO(field: any): FieldConfigDTO {
|
||||
const uiMetadata = field.uiMetadata || {};
|
||||
|
||||
return {
|
||||
id: field.id,
|
||||
apiName: field.apiName,
|
||||
label: field.label,
|
||||
type: this.mapFieldType(field.type),
|
||||
|
||||
// Display properties
|
||||
placeholder: uiMetadata.placeholder || field.description,
|
||||
helpText: uiMetadata.helpText || field.description,
|
||||
defaultValue: field.defaultValue,
|
||||
|
||||
// Validation
|
||||
isRequired: field.isRequired || false,
|
||||
isReadOnly: field.isSystem || uiMetadata.isReadOnly || false,
|
||||
|
||||
// View visibility
|
||||
showOnList: uiMetadata.showOnList !== false,
|
||||
showOnDetail: uiMetadata.showOnDetail !== false,
|
||||
showOnEdit: uiMetadata.showOnEdit !== false && !field.isSystem,
|
||||
sortable: uiMetadata.sortable !== false,
|
||||
|
||||
// Field type specific options
|
||||
options: uiMetadata.options,
|
||||
rows: uiMetadata.rows,
|
||||
min: uiMetadata.min,
|
||||
max: uiMetadata.max,
|
||||
step: uiMetadata.step,
|
||||
accept: uiMetadata.accept,
|
||||
relationObject: field.referenceObject,
|
||||
relationDisplayField: uiMetadata.relationDisplayField,
|
||||
|
||||
// Formatting
|
||||
format: uiMetadata.format,
|
||||
prefix: uiMetadata.prefix,
|
||||
suffix: uiMetadata.suffix,
|
||||
|
||||
// Validation rules
|
||||
validationRules: this.buildValidationRules(field, uiMetadata),
|
||||
|
||||
// Advanced
|
||||
dependsOn: uiMetadata.dependsOn,
|
||||
computedValue: uiMetadata.computedValue,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Map database field type to frontend FieldType enum
|
||||
*/
|
||||
private mapFieldType(dbType: string): string {
|
||||
const typeMap: Record<string, string> = {
|
||||
'string': 'text',
|
||||
'text': 'textarea',
|
||||
'integer': 'number',
|
||||
'decimal': 'number',
|
||||
'boolean': 'boolean',
|
||||
'date': 'date',
|
||||
'datetime': 'datetime',
|
||||
'time': 'time',
|
||||
'email': 'email',
|
||||
'url': 'url',
|
||||
'phone': 'text',
|
||||
'picklist': 'select',
|
||||
'multipicklist': 'multiSelect',
|
||||
'lookup': 'belongsTo',
|
||||
'master-detail': 'belongsTo',
|
||||
'currency': 'currency',
|
||||
'percent': 'number',
|
||||
'textarea': 'textarea',
|
||||
'richtext': 'markdown',
|
||||
'file': 'file',
|
||||
'image': 'image',
|
||||
'json': 'json',
|
||||
};
|
||||
|
||||
return typeMap[dbType.toLowerCase()] || 'text';
|
||||
}
|
||||
|
||||
/**
|
||||
* Build validation rules array
|
||||
*/
|
||||
private buildValidationRules(field: any, uiMetadata: any): Array<any> {
|
||||
const rules = uiMetadata.validationRules || [];
|
||||
|
||||
// Add required rule if field is required and not already in rules
|
||||
if (field.isRequired && !rules.some(r => r.type === 'required')) {
|
||||
rules.unshift({
|
||||
type: 'required',
|
||||
message: `${field.label} is required`,
|
||||
});
|
||||
}
|
||||
|
||||
// Add length validation for string fields
|
||||
if (field.length && field.type === 'string') {
|
||||
rules.push({
|
||||
type: 'max',
|
||||
value: field.length,
|
||||
message: `${field.label} must not exceed ${field.length} characters`,
|
||||
});
|
||||
}
|
||||
|
||||
// Add email validation
|
||||
if (field.type === 'email' && !rules.some(r => r.type === 'email')) {
|
||||
rules.push({
|
||||
type: 'email',
|
||||
message: `${field.label} must be a valid email address`,
|
||||
});
|
||||
}
|
||||
|
||||
// Add URL validation
|
||||
if (field.type === 'url' && !rules.some(r => r.type === 'url')) {
|
||||
rules.push({
|
||||
type: 'url',
|
||||
message: `${field.label} must be a valid URL`,
|
||||
});
|
||||
}
|
||||
|
||||
return rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert object definition with fields to DTO
|
||||
*/
|
||||
mapObjectDefinitionToDTO(objectDef: any): ObjectDefinitionDTO {
|
||||
return {
|
||||
id: objectDef.id,
|
||||
apiName: objectDef.apiName,
|
||||
label: objectDef.label,
|
||||
pluralLabel: objectDef.pluralLabel,
|
||||
description: objectDef.description,
|
||||
isSystem: objectDef.isSystem || false,
|
||||
fields: (objectDef.fields || [])
|
||||
.filter((f: any) => f.isActive !== false)
|
||||
.sort((a: any, b: any) => (a.displayOrder || 0) - (b.displayOrder || 0))
|
||||
.map((f: any) => this.mapFieldToDTO(f)),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate default UI metadata for a field type
|
||||
*/
|
||||
generateDefaultUIMetadata(fieldType: string): any {
|
||||
const defaults: Record<string, any> = {
|
||||
text: {
|
||||
showOnList: true,
|
||||
showOnDetail: true,
|
||||
showOnEdit: true,
|
||||
sortable: true,
|
||||
},
|
||||
textarea: {
|
||||
showOnList: false,
|
||||
showOnDetail: true,
|
||||
showOnEdit: true,
|
||||
sortable: false,
|
||||
rows: 4,
|
||||
},
|
||||
number: {
|
||||
showOnList: true,
|
||||
showOnDetail: true,
|
||||
showOnEdit: true,
|
||||
sortable: true,
|
||||
},
|
||||
currency: {
|
||||
showOnList: true,
|
||||
showOnDetail: true,
|
||||
showOnEdit: true,
|
||||
sortable: true,
|
||||
prefix: '$',
|
||||
step: 0.01,
|
||||
},
|
||||
boolean: {
|
||||
showOnList: true,
|
||||
showOnDetail: true,
|
||||
showOnEdit: true,
|
||||
sortable: true,
|
||||
},
|
||||
date: {
|
||||
showOnList: true,
|
||||
showOnDetail: true,
|
||||
showOnEdit: true,
|
||||
sortable: true,
|
||||
format: 'yyyy-MM-dd',
|
||||
},
|
||||
datetime: {
|
||||
showOnList: false,
|
||||
showOnDetail: true,
|
||||
showOnEdit: true,
|
||||
sortable: true,
|
||||
format: 'yyyy-MM-dd HH:mm:ss',
|
||||
},
|
||||
email: {
|
||||
showOnList: true,
|
||||
showOnDetail: true,
|
||||
showOnEdit: true,
|
||||
sortable: true,
|
||||
validationRules: [{ type: 'email' }],
|
||||
},
|
||||
url: {
|
||||
showOnList: false,
|
||||
showOnDetail: true,
|
||||
showOnEdit: true,
|
||||
sortable: false,
|
||||
validationRules: [{ type: 'url' }],
|
||||
},
|
||||
select: {
|
||||
showOnList: true,
|
||||
showOnDetail: true,
|
||||
showOnEdit: true,
|
||||
sortable: true,
|
||||
options: [],
|
||||
},
|
||||
multiSelect: {
|
||||
showOnList: false,
|
||||
showOnDetail: true,
|
||||
showOnEdit: true,
|
||||
sortable: false,
|
||||
options: [],
|
||||
},
|
||||
image: {
|
||||
showOnList: false,
|
||||
showOnDetail: true,
|
||||
showOnEdit: true,
|
||||
sortable: false,
|
||||
accept: 'image/*',
|
||||
},
|
||||
file: {
|
||||
showOnList: false,
|
||||
showOnDetail: true,
|
||||
showOnEdit: true,
|
||||
sortable: false,
|
||||
},
|
||||
};
|
||||
|
||||
return defaults[fieldType] || {
|
||||
showOnList: true,
|
||||
showOnDetail: true,
|
||||
showOnEdit: true,
|
||||
sortable: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user