WIP - display related lists

This commit is contained in:
Francisco Gaona
2026-01-09 07:49:30 +01:00
parent 2075fec183
commit 852c4e28d2
8 changed files with 51 additions and 7 deletions

View File

@@ -135,7 +135,7 @@ export class DynamicModelFactory {
} }
} }
const targetTable = this.getTableName(relation.targetObjectApiName); const targetTable = DynamicModelFactory.getTableName(relation.targetObjectApiName);
if (relation.type === 'belongsTo') { if (relation.type === 'belongsTo') {
mappings[relation.name] = { mappings[relation.name] = {

View File

@@ -643,6 +643,7 @@ export class ObjectService {
relationName: string; relationName: string;
objectApiName: string; objectApiName: string;
lookupFieldApiName: string; lookupFieldApiName: string;
parentObjectApiName: string;
fields: any[]; fields: any[];
}>> { }>> {
const knex = await this.tenantDbService.getTenantKnexById(tenantId); const knex = await this.tenantDbService.getTenantKnexById(tenantId);
@@ -703,6 +704,7 @@ export class ObjectService {
relationName, relationName,
objectApiName: lookup.childApiName, objectApiName: lookup.childApiName,
lookupFieldApiName: lookup.fieldApiName, lookupFieldApiName: lookup.fieldApiName,
parentObjectApiName: objectApiName,
fields: fieldsByObject.get(lookup.objectDefinitionId) || [], fields: fieldsByObject.get(lookup.objectDefinitionId) || [],
}; };
}); });

View File

@@ -11,6 +11,8 @@ interface RelatedListConfig {
relationName: string // e.g., 'domains', 'users' relationName: string // e.g., 'domains', 'users'
objectApiName: string // e.g., 'domains', 'users' objectApiName: string // e.g., 'domains', 'users'
fields: FieldConfig[] // Fields to display in the list fields: FieldConfig[] // Fields to display in the list
lookupFieldApiName?: string // Used to filter by parentId when fetching
parentObjectApiName?: string // Parent object API name, used to derive lookup field if missing
canCreate?: boolean canCreate?: boolean
createRoute?: string // Route to create new related record createRoute?: string // Route to create new related record
} }
@@ -19,11 +21,11 @@ interface Props {
config: RelatedListConfig config: RelatedListConfig
parentId: string parentId: string
relatedRecords?: any[] // Can be passed in if already fetched relatedRecords?: any[] // Can be passed in if already fetched
baseUrl?: string // Base API URL, defaults to '/central' baseUrl?: string // Base API URL, defaults to runtime objects
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
baseUrl: '/central', baseUrl: '/runtime/objects',
relatedRecords: undefined, relatedRecords: undefined,
}) })
@@ -53,14 +55,48 @@ const fetchRelatedRecords = async () => {
try { try {
// Replace :parentId placeholder in the API path // Replace :parentId placeholder in the API path
let apiPath = props.config.objectApiName.replace(':parentId', props.parentId) const sanitizedBase = props.baseUrl.replace(/\/$/, '')
let apiPath = props.config.objectApiName.replace(':parentId', props.parentId).replace(/^\/+/, '')
const isRuntimeObjects = sanitizedBase.endsWith('/runtime/objects')
// Default runtime object routes expect /:objectApiName/records
if (isRuntimeObjects && !apiPath.includes('/')) {
apiPath = `${apiPath}/records`
}
const response = await api.get(`${props.baseUrl}/${apiPath}`, { const findLookupKey = () => {
if (props.config.lookupFieldApiName) return props.config.lookupFieldApiName
const parentName = props.config.parentObjectApiName?.toLowerCase()
const fields = props.config.fields || []
const parentMatch = fields.find(field => {
const relation = (field as any).relationObject || (field as any).referenceObject
return relation && parentName && relation.toLowerCase() === parentName
})
if (parentMatch?.apiName) return parentMatch.apiName
const lookupMatch = fields.find(
field => (field.type || '').toString().toLowerCase() === 'lookup'
)
if (lookupMatch?.apiName) return lookupMatch.apiName
const idMatch = fields.find(field =>
field.apiName?.toLowerCase().endsWith('id')
)
if (idMatch?.apiName) return idMatch.apiName
return 'parentId'
}
const lookupKey = findLookupKey()
const response = await api.get(`${sanitizedBase}/${apiPath}`, {
params: { params: {
parentId: props.parentId, [lookupKey]: props.parentId,
}, },
}) })
records.value = response || [] records.value = response?.data || response || []
} catch (err: any) { } catch (err: any) {
console.error('Error fetching related records:', err) console.error('Error fetching related records:', err)
error.value = err.message || 'Failed to fetch related records' error.value = err.message || 'Failed to fetch related records'

View File

@@ -247,6 +247,7 @@ const visibleRelatedLists = computed<RelatedListConfig[]>(() => {
:config="relatedList" :config="relatedList"
:parent-id="data.id" :parent-id="data.id"
:related-records="data[relatedList.relationName]" :related-records="data[relatedList.relationName]"
:base-url="baseUrl"
@navigate="(objectApiName, recordId) => emit('navigate', objectApiName, recordId)" @navigate="(objectApiName, recordId) => emit('navigate', objectApiName, recordId)"
@create="(objectApiName, parentId) => emit('createRelated', objectApiName, parentId)" @create="(objectApiName, parentId) => emit('createRelated', objectApiName, parentId)"
/> />

View File

@@ -133,6 +133,7 @@ onMounted(async () => {
:config="domainDetailConfig" :config="domainDetailConfig"
:data="currentRecord" :data="currentRecord"
:loading="dataLoading" :loading="dataLoading"
base-url="/central"
@edit="handleEdit" @edit="handleEdit"
@delete="() => handleDelete([currentRecord])" @delete="() => handleDelete([currentRecord])"
@back="handleBack" @back="handleBack"

View File

@@ -168,6 +168,7 @@ onMounted(async () => {
:config="tenantDetailConfig" :config="tenantDetailConfig"
:data="currentRecord" :data="currentRecord"
:loading="dataLoading" :loading="dataLoading"
base-url="/central"
@edit="handleEdit" @edit="handleEdit"
@delete="() => handleDelete([currentRecord])" @delete="() => handleDelete([currentRecord])"
@back="handleBack" @back="handleBack"

View File

@@ -138,6 +138,7 @@ onMounted(async () => {
:config="centralUserDetailConfig" :config="centralUserDetailConfig"
:data="currentRecord" :data="currentRecord"
:loading="dataLoading" :loading="dataLoading"
base-url="/central"
@edit="handleEdit" @edit="handleEdit"
@delete="() => handleDelete([currentRecord])" @delete="() => handleDelete([currentRecord])"
@back="handleBack" @back="handleBack"

View File

@@ -123,6 +123,8 @@ export interface RelatedListConfig {
relationName: string; relationName: string;
objectApiName: string; objectApiName: string;
fields: FieldConfig[]; fields: FieldConfig[];
lookupFieldApiName?: string;
parentObjectApiName?: string;
canCreate?: boolean; canCreate?: boolean;
createRoute?: string; createRoute?: string;
} }