Add record access strategy
This commit is contained in:
411
frontend/composables/useCentralEntities.ts
Normal file
411
frontend/composables/useCentralEntities.ts
Normal file
@@ -0,0 +1,411 @@
|
||||
/**
|
||||
* Static field configurations for central database entities
|
||||
* These entities don't have dynamic field definitions like tenant objects
|
||||
*/
|
||||
|
||||
import { FieldType, ViewMode } from '@/types/field-types'
|
||||
import type { FieldConfig, ListViewConfig, DetailViewConfig, EditViewConfig, RelatedListConfig } from '@/types/field-types'
|
||||
|
||||
// ==================== TENANTS ====================
|
||||
|
||||
export const tenantFields: FieldConfig[] = [
|
||||
{
|
||||
id: 'name',
|
||||
apiName: 'name',
|
||||
label: 'Tenant Name',
|
||||
type: FieldType.TEXT,
|
||||
isRequired: true,
|
||||
showOnList: true,
|
||||
showOnDetail: true,
|
||||
showOnEdit: true,
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
id: 'slug',
|
||||
apiName: 'slug',
|
||||
label: 'Slug',
|
||||
type: FieldType.TEXT,
|
||||
isRequired: false,
|
||||
showOnList: true,
|
||||
showOnDetail: true,
|
||||
showOnEdit: true,
|
||||
sortable: true,
|
||||
helpText: 'Unique identifier for the tenant (auto-generated from name if not provided)',
|
||||
},
|
||||
{
|
||||
id: 'primaryDomain',
|
||||
apiName: 'primaryDomain',
|
||||
label: 'Primary Domain',
|
||||
type: FieldType.TEXT,
|
||||
isRequired: true,
|
||||
showOnList: false,
|
||||
showOnDetail: false,
|
||||
showOnEdit: true,
|
||||
helpText: 'Primary subdomain for this tenant (e.g., "acme" for acme.yourdomain.com)',
|
||||
},
|
||||
{
|
||||
id: 'status',
|
||||
apiName: 'status',
|
||||
label: 'Status',
|
||||
type: FieldType.SELECT,
|
||||
isRequired: true,
|
||||
showOnList: true,
|
||||
showOnDetail: true,
|
||||
showOnEdit: true,
|
||||
options: [
|
||||
{ label: 'Active', value: 'active' },
|
||||
{ label: 'Suspended', value: 'suspended' },
|
||||
{ label: 'Deleted', value: 'deleted' },
|
||||
],
|
||||
defaultValue: 'active',
|
||||
},
|
||||
{
|
||||
id: 'dbHost',
|
||||
apiName: 'dbHost',
|
||||
label: 'Database Host',
|
||||
type: FieldType.TEXT,
|
||||
isRequired: false,
|
||||
showOnList: false,
|
||||
showOnDetail: true,
|
||||
showOnEdit: true,
|
||||
helpText: 'Leave blank to use default database host',
|
||||
},
|
||||
{
|
||||
id: 'dbPort',
|
||||
apiName: 'dbPort',
|
||||
label: 'Database Port',
|
||||
type: FieldType.NUMBER,
|
||||
isRequired: false,
|
||||
showOnList: false,
|
||||
showOnDetail: true,
|
||||
showOnEdit: true,
|
||||
defaultValue: 3306,
|
||||
helpText: 'Leave blank to use default port (3306)',
|
||||
},
|
||||
{
|
||||
id: 'dbName',
|
||||
apiName: 'dbName',
|
||||
label: 'Database Name',
|
||||
type: FieldType.TEXT,
|
||||
isRequired: false,
|
||||
showOnList: false,
|
||||
showOnDetail: true,
|
||||
showOnEdit: false,
|
||||
helpText: 'Auto-generated based on tenant slug',
|
||||
},
|
||||
{
|
||||
id: 'dbUsername',
|
||||
apiName: 'dbUsername',
|
||||
label: 'Database Username',
|
||||
type: FieldType.TEXT,
|
||||
isRequired: false,
|
||||
showOnList: false,
|
||||
showOnDetail: true,
|
||||
showOnEdit: false,
|
||||
helpText: 'Auto-generated based on tenant slug',
|
||||
},
|
||||
{
|
||||
id: 'createdAt',
|
||||
apiName: 'createdAt',
|
||||
label: 'Created At',
|
||||
type: FieldType.DATETIME,
|
||||
showOnList: true,
|
||||
showOnDetail: true,
|
||||
showOnEdit: false,
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
id: 'updatedAt',
|
||||
apiName: 'updatedAt',
|
||||
label: 'Updated At',
|
||||
type: FieldType.DATETIME,
|
||||
showOnList: false,
|
||||
showOnDetail: true,
|
||||
showOnEdit: false,
|
||||
},
|
||||
]
|
||||
|
||||
export const tenantListConfig: ListViewConfig = {
|
||||
objectApiName: 'Tenant',
|
||||
mode: ViewMode.LIST,
|
||||
fields: tenantFields,
|
||||
pageSize: 25,
|
||||
searchable: true,
|
||||
filterable: true,
|
||||
exportable: true,
|
||||
}
|
||||
|
||||
export const tenantDetailConfig: DetailViewConfig = {
|
||||
objectApiName: 'Tenant',
|
||||
mode: ViewMode.DETAIL,
|
||||
fields: tenantFields,
|
||||
sections: [
|
||||
{
|
||||
title: 'Basic Information',
|
||||
fields: ['name', 'slug', 'status'],
|
||||
},
|
||||
{
|
||||
title: 'Database Configuration',
|
||||
fields: ['dbHost', 'dbPort', 'dbName', 'dbUsername'],
|
||||
collapsible: true,
|
||||
},
|
||||
{
|
||||
title: 'System Information',
|
||||
fields: ['createdAt', 'updatedAt'],
|
||||
collapsible: true,
|
||||
},
|
||||
],
|
||||
relatedLists: [
|
||||
{
|
||||
title: 'Domains',
|
||||
relationName: 'domains',
|
||||
objectApiName: 'domains',
|
||||
fields: [
|
||||
{ id: 'domain', apiName: 'domain', label: 'Domain', type: FieldType.TEXT },
|
||||
{ id: 'isPrimary', apiName: 'isPrimary', label: 'Primary', type: FieldType.BOOLEAN },
|
||||
{ id: 'createdAt', apiName: 'createdAt', label: 'Created', type: FieldType.DATETIME },
|
||||
],
|
||||
canCreate: true,
|
||||
},
|
||||
{
|
||||
title: 'Tenant Users',
|
||||
relationName: 'users',
|
||||
objectApiName: 'tenants/:parentId/users',
|
||||
fields: [
|
||||
{ id: 'email', apiName: 'email', label: 'Email', type: FieldType.EMAIL },
|
||||
{ id: 'firstName', apiName: 'firstName', label: 'First Name', type: FieldType.TEXT },
|
||||
{ id: 'lastName', apiName: 'lastName', label: 'Last Name', type: FieldType.TEXT },
|
||||
{ id: 'createdAt', apiName: 'createdAt', label: 'Created', type: FieldType.DATETIME },
|
||||
],
|
||||
canCreate: true,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export const tenantEditConfig: EditViewConfig = {
|
||||
objectApiName: 'Tenant',
|
||||
mode: ViewMode.EDIT,
|
||||
fields: tenantFields,
|
||||
sections: [
|
||||
{
|
||||
title: 'Basic Information',
|
||||
fields: ['name', 'slug', 'primaryDomain', 'status'],
|
||||
},
|
||||
{
|
||||
title: 'Advanced Options',
|
||||
description: 'Optional database configuration (leave blank for defaults)',
|
||||
fields: ['dbHost', 'dbPort'],
|
||||
collapsible: true,
|
||||
defaultCollapsed: true,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
// ==================== DOMAINS ====================
|
||||
|
||||
export const domainFields: FieldConfig[] = [
|
||||
{
|
||||
id: 'domain',
|
||||
apiName: 'domain',
|
||||
label: 'Domain',
|
||||
type: FieldType.TEXT,
|
||||
isRequired: true,
|
||||
showOnList: true,
|
||||
showOnDetail: true,
|
||||
showOnEdit: true,
|
||||
sortable: true,
|
||||
helpText: 'Subdomain for this tenant (e.g., "acme" for acme.yourapp.com)',
|
||||
},
|
||||
{
|
||||
id: 'tenantId',
|
||||
apiName: 'tenantId',
|
||||
label: 'Tenant',
|
||||
type: FieldType.BELONGS_TO,
|
||||
isRequired: true,
|
||||
showOnList: true,
|
||||
showOnDetail: true,
|
||||
showOnEdit: true,
|
||||
relationObject: 'tenants',
|
||||
relationDisplayField: 'name',
|
||||
},
|
||||
{
|
||||
id: 'isPrimary',
|
||||
apiName: 'isPrimary',
|
||||
label: 'Primary Domain',
|
||||
type: FieldType.BOOLEAN,
|
||||
showOnList: true,
|
||||
showOnDetail: true,
|
||||
showOnEdit: true,
|
||||
defaultValue: false,
|
||||
helpText: 'Mark as the primary domain for this tenant',
|
||||
},
|
||||
{
|
||||
id: 'createdAt',
|
||||
apiName: 'createdAt',
|
||||
label: 'Created At',
|
||||
type: FieldType.DATETIME,
|
||||
showOnList: true,
|
||||
showOnDetail: true,
|
||||
showOnEdit: false,
|
||||
sortable: true,
|
||||
},
|
||||
]
|
||||
|
||||
export const domainListConfig: ListViewConfig = {
|
||||
objectApiName: 'Domain',
|
||||
mode: ViewMode.LIST,
|
||||
fields: domainFields,
|
||||
pageSize: 25,
|
||||
searchable: true,
|
||||
filterable: true,
|
||||
exportable: true,
|
||||
}
|
||||
|
||||
export const domainDetailConfig: DetailViewConfig = {
|
||||
objectApiName: 'Domain',
|
||||
mode: ViewMode.DETAIL,
|
||||
fields: domainFields,
|
||||
sections: [
|
||||
{
|
||||
title: 'Domain Information',
|
||||
fields: ['domain', 'tenantId', 'isPrimary', 'createdAt'],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export const domainEditConfig: EditViewConfig = {
|
||||
objectApiName: 'Domain',
|
||||
mode: ViewMode.EDIT,
|
||||
fields: domainFields,
|
||||
sections: [
|
||||
{
|
||||
title: 'Domain Configuration',
|
||||
fields: ['domain', 'tenantId', 'isPrimary'],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
// ==================== USERS (Central Admin Users) ====================
|
||||
|
||||
export const centralUserFields: FieldConfig[] = [
|
||||
{
|
||||
id: 'email',
|
||||
apiName: 'email',
|
||||
label: 'Email',
|
||||
type: FieldType.EMAIL,
|
||||
isRequired: true,
|
||||
showOnList: true,
|
||||
showOnDetail: true,
|
||||
showOnEdit: true,
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
id: 'firstName',
|
||||
apiName: 'firstName',
|
||||
label: 'First Name',
|
||||
type: FieldType.TEXT,
|
||||
showOnList: true,
|
||||
showOnDetail: true,
|
||||
showOnEdit: true,
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
id: 'lastName',
|
||||
apiName: 'lastName',
|
||||
label: 'Last Name',
|
||||
type: FieldType.TEXT,
|
||||
showOnList: true,
|
||||
showOnDetail: true,
|
||||
showOnEdit: true,
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
id: 'password',
|
||||
apiName: 'password',
|
||||
label: 'Password',
|
||||
type: FieldType.TEXT, // Will be treated as password in edit view
|
||||
isRequired: true,
|
||||
showOnList: false,
|
||||
showOnDetail: false,
|
||||
showOnEdit: true,
|
||||
helpText: 'Leave blank to keep existing password',
|
||||
},
|
||||
{
|
||||
id: 'role',
|
||||
apiName: 'role',
|
||||
label: 'Role',
|
||||
type: FieldType.SELECT,
|
||||
isRequired: true,
|
||||
showOnList: true,
|
||||
showOnDetail: true,
|
||||
showOnEdit: true,
|
||||
options: [
|
||||
{ label: 'Admin', value: 'admin' },
|
||||
{ label: 'Super Admin', value: 'superadmin' },
|
||||
],
|
||||
defaultValue: 'admin',
|
||||
},
|
||||
{
|
||||
id: 'isActive',
|
||||
apiName: 'isActive',
|
||||
label: 'Active',
|
||||
type: FieldType.BOOLEAN,
|
||||
showOnList: true,
|
||||
showOnDetail: true,
|
||||
showOnEdit: true,
|
||||
defaultValue: true,
|
||||
},
|
||||
{
|
||||
id: 'createdAt',
|
||||
apiName: 'createdAt',
|
||||
label: 'Created At',
|
||||
type: FieldType.DATETIME,
|
||||
showOnList: true,
|
||||
showOnDetail: true,
|
||||
showOnEdit: false,
|
||||
sortable: true,
|
||||
},
|
||||
]
|
||||
|
||||
export const centralUserListConfig: ListViewConfig = {
|
||||
objectApiName: 'User',
|
||||
mode: ViewMode.LIST,
|
||||
fields: centralUserFields,
|
||||
pageSize: 25,
|
||||
searchable: true,
|
||||
filterable: true,
|
||||
exportable: true,
|
||||
}
|
||||
|
||||
export const centralUserDetailConfig: DetailViewConfig = {
|
||||
objectApiName: 'User',
|
||||
mode: ViewMode.DETAIL,
|
||||
fields: centralUserFields,
|
||||
sections: [
|
||||
{
|
||||
title: 'User Information',
|
||||
fields: ['email', 'firstName', 'lastName', 'role', 'isActive'],
|
||||
},
|
||||
{
|
||||
title: 'System Information',
|
||||
fields: ['createdAt'],
|
||||
collapsible: true,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export const centralUserEditConfig: EditViewConfig = {
|
||||
objectApiName: 'User',
|
||||
mode: ViewMode.EDIT,
|
||||
fields: centralUserFields,
|
||||
sections: [
|
||||
{
|
||||
title: 'User Information',
|
||||
fields: ['email', 'firstName', 'lastName'],
|
||||
},
|
||||
{
|
||||
title: 'Access & Security',
|
||||
fields: ['password', 'role', 'isActive'],
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -13,8 +13,12 @@ export const useFields = () => {
|
||||
// Convert isSystem to boolean (handle 0/1 from database)
|
||||
const isSystemField = Boolean(fieldDef.isSystem)
|
||||
|
||||
// Only truly system fields (id, createdAt, updatedAt, etc.) should be hidden on edit
|
||||
const isAutoGeneratedField = ['id', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'].includes(fieldDef.apiName)
|
||||
// Define all system/auto-generated field names
|
||||
const systemFieldNames = ['id', 'createdAt', 'updatedAt', 'created_at', 'updated_at', 'createdBy', 'updatedBy', 'tenantId', 'ownerId']
|
||||
const isAutoGeneratedField = systemFieldNames.includes(fieldDef.apiName)
|
||||
|
||||
// Hide system fields and auto-generated fields on edit
|
||||
const shouldHideOnEdit = isSystemField || isAutoGeneratedField
|
||||
|
||||
return {
|
||||
id: fieldDef.id,
|
||||
@@ -23,35 +27,35 @@ export const useFields = () => {
|
||||
type: fieldDef.type,
|
||||
|
||||
// Default values
|
||||
placeholder: fieldDef.uiMetadata?.placeholder || fieldDef.description,
|
||||
helpText: fieldDef.uiMetadata?.helpText || fieldDef.description,
|
||||
placeholder: fieldDef.placeholder || fieldDef.description,
|
||||
helpText: fieldDef.helpText || fieldDef.description,
|
||||
defaultValue: fieldDef.defaultValue,
|
||||
|
||||
// Validation
|
||||
isRequired: fieldDef.isRequired,
|
||||
isReadOnly: isAutoGeneratedField || fieldDef.uiMetadata?.isReadOnly,
|
||||
validationRules: fieldDef.uiMetadata?.validationRules || [],
|
||||
isReadOnly: isAutoGeneratedField || fieldDef.isReadOnly,
|
||||
validationRules: fieldDef.validationRules || [],
|
||||
|
||||
// View options - only hide auto-generated fields by default
|
||||
showOnList: fieldDef.uiMetadata?.showOnList ?? true,
|
||||
showOnDetail: fieldDef.uiMetadata?.showOnDetail ?? true,
|
||||
showOnEdit: fieldDef.uiMetadata?.showOnEdit ?? !isAutoGeneratedField,
|
||||
sortable: fieldDef.uiMetadata?.sortable ?? true,
|
||||
// View options - only hide system and auto-generated fields by default
|
||||
showOnList: fieldDef.showOnList ?? true,
|
||||
showOnDetail: fieldDef.showOnDetail ?? true,
|
||||
showOnEdit: fieldDef.showOnEdit ?? !shouldHideOnEdit,
|
||||
sortable: fieldDef.sortable ?? true,
|
||||
|
||||
// Field type specific
|
||||
options: fieldDef.uiMetadata?.options,
|
||||
rows: fieldDef.uiMetadata?.rows,
|
||||
min: fieldDef.uiMetadata?.min,
|
||||
max: fieldDef.uiMetadata?.max,
|
||||
step: fieldDef.uiMetadata?.step,
|
||||
accept: fieldDef.uiMetadata?.accept,
|
||||
relationObject: fieldDef.referenceObject,
|
||||
relationDisplayField: fieldDef.uiMetadata?.relationDisplayField,
|
||||
options: fieldDef.options,
|
||||
rows: fieldDef.rows,
|
||||
min: fieldDef.min,
|
||||
max: fieldDef.max,
|
||||
step: fieldDef.step,
|
||||
accept: fieldDef.accept,
|
||||
relationObject: fieldDef.relationObject,
|
||||
relationDisplayField: fieldDef.relationDisplayField,
|
||||
|
||||
// Formatting
|
||||
format: fieldDef.uiMetadata?.format,
|
||||
prefix: fieldDef.uiMetadata?.prefix,
|
||||
suffix: fieldDef.uiMetadata?.suffix,
|
||||
format: fieldDef.format,
|
||||
prefix: fieldDef.prefix,
|
||||
suffix: fieldDef.suffix,
|
||||
|
||||
// Advanced
|
||||
dependsOn: fieldDef.uiMetadata?.dependsOn,
|
||||
|
||||
Reference in New Issue
Block a user